forked from mirrors/tftp-hpa-google
		
	remap: fix timeouts for "g", add a "gg" flag to match sed s///g
Make sure that when using the global option, we still bump the deadman timer. The "g" option really should only have applied to the right-hand unmatched part of the string, like in sed. Add a "gg" option which does that. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
		
							parent
							
								
									74c5d8a020
								
							
						
					
					
						commit
						ac7f98e4d8
					
				
					 2 changed files with 189 additions and 119 deletions
				
			
		
							
								
								
									
										287
									
								
								tftpd/remap.c
									
										
									
									
									
								
							
							
						
						
									
										287
									
								
								tftpd/remap.c
									
										
									
									
									
								
							|  | @ -22,7 +22,7 @@ | |||
| #include "tftpd.h" | ||||
| #include "remap.h" | ||||
| 
 | ||||
| #define DEADMAN_MAX_STEPS	1024    /* Timeout after this many steps */ | ||||
| #define DEADMAN_MAX_STEPS	4096    /* Timeout after this many steps */ | ||||
| #define MAXLINE			16384   /* Truncate a line at this many bytes */ | ||||
| 
 | ||||
| #define RULE_REWRITE	0x01    /* This is a rewrite rule */ | ||||
|  | @ -37,6 +37,7 @@ | |||
| #define RULE_HASFILE	0x100	/* Valid if rule results in a valid filename */ | ||||
| #define RULE_RRQ	0x200	/* Get (read) only */ | ||||
| #define RULE_WRQ	0x400	/* Put (write) only */ | ||||
| #define RULE_SEDG	0x800   /* sed-style global */ | ||||
| 
 | ||||
| struct rule { | ||||
|     struct rule *next; | ||||
|  | @ -61,22 +62,30 @@ static int xform_tolower(int c) | |||
|     return tolower(c); | ||||
| } | ||||
| 
 | ||||
| /* Do \-substitution.  Call with string == NULL to get length only. */ | ||||
| static int genmatchstring(char *string, const char *pattern, | ||||
|                           const char *input, const regmatch_t * pmatch, | ||||
|                           match_pattern_callback macrosub) | ||||
| /*
 | ||||
|  * Do \-substitution.  Call with string == NULL to get length only. | ||||
|  * "start" indicates an offset into the input buffer where the pattern | ||||
|  * match was started. | ||||
|  */ | ||||
| static int do_genmatchstring(char *string, const char *pattern, | ||||
|                              const char *ibuf, | ||||
|                              const regmatch_t * pmatch, | ||||
|                              match_pattern_callback macrosub, | ||||
|                              int start, int *nextp) | ||||
| { | ||||
|     int (*xform) (int) = xform_null; | ||||
|     int len = 0; | ||||
|     int n, mlen, sublen; | ||||
|     int endbytes; | ||||
|     const char *input = ibuf + start; | ||||
| 
 | ||||
|     /* Get section before match; note pmatch[0] is the whole match */ | ||||
|     endbytes = strlen(input) - pmatch[0].rm_eo; | ||||
|     len = pmatch[0].rm_so + endbytes; | ||||
|     len = start + pmatch[0].rm_so; | ||||
|     if (string) { | ||||
|         memcpy(string, input, pmatch[0].rm_so); | ||||
|         string += pmatch[0].rm_so; | ||||
|         /* Copy the prefix before "start" as well! */ | ||||
|         memcpy(string, ibuf, start + pmatch[0].rm_so); | ||||
|         string += start + pmatch[0].rm_so; | ||||
|     } | ||||
| 
 | ||||
|     /* Transform matched section */ | ||||
|  | @ -102,7 +111,7 @@ static int genmatchstring(char *string, const char *pattern, | |||
|                     mlen = pmatch[n].rm_eo - pmatch[n].rm_so; | ||||
|                     len += mlen; | ||||
|                     if (string) { | ||||
|                         const char *p = input + pmatch[n].rm_so; | ||||
|                         const char *p = input + start + pmatch[n].rm_so; | ||||
|                         while (mlen--) | ||||
|                             *string++ = xform(*p++); | ||||
|                     } | ||||
|  | @ -145,7 +154,12 @@ static int genmatchstring(char *string, const char *pattern, | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Pointer to post-substitution tail */ | ||||
|     if (nextp) | ||||
|         *nextp = len; | ||||
| 
 | ||||
|     /* Copy section after match */ | ||||
|     len += endbytes; | ||||
|     if (string) { | ||||
|         memcpy(string, input + pmatch[0].rm_eo, endbytes); | ||||
|         string[endbytes] = '\0'; | ||||
|  | @ -154,6 +168,26 @@ static int genmatchstring(char *string, const char *pattern, | |||
|     return len; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Ditto, but allocate the string in a new buffer | ||||
|  */ | ||||
| 
 | ||||
| static int genmatchstring(char **string, const char *pattern, | ||||
|                           const char *ibuf, | ||||
|                           const regmatch_t * pmatch, | ||||
|                           match_pattern_callback macrosub, | ||||
|                           int start, int *nextp) | ||||
| { | ||||
|     int len; | ||||
|     char *buf; | ||||
| 
 | ||||
|     len = do_genmatchstring(NULL, pattern, ibuf, pmatch, | ||||
|                             macrosub, start, NULL); | ||||
|     buf = tf_malloc(len + 1); | ||||
|     return do_genmatchstring(buf, pattern, ibuf, pmatch, | ||||
|                              macrosub, start, nextp); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Extract a string terminated by non-escaped whitespace; ignoring | ||||
|  * leading whitespace.  Consider an unescaped # to be a comment marker, | ||||
|  | @ -165,7 +199,7 @@ static int readescstring(char *buf, char **str) | |||
|     int wasbs = 0, len = 0; | ||||
| 
 | ||||
|     while (*p && isspace(*p)) | ||||
|        p++; | ||||
|         p++; | ||||
| 
 | ||||
|     if (!*p) { | ||||
|         *buf = '\0'; | ||||
|  | @ -211,14 +245,17 @@ static int parseline(char *line, struct rule *r, int lineno) | |||
|             r->rule_flags |= RULE_REWRITE; | ||||
|             break; | ||||
|         case 'g': | ||||
|             r->rule_flags |= RULE_GLOBAL; | ||||
|             if (r->rule_flags & RULE_GLOBAL) | ||||
|                 r->rule_flags |= RULE_SEDG; | ||||
|             else | ||||
|                 r->rule_flags |= RULE_GLOBAL; | ||||
|             break; | ||||
|         case 'e': | ||||
|             r->rule_flags |= RULE_EXIT; | ||||
|             break; | ||||
| 	case 'E': | ||||
| 	    r->rule_flags |= RULE_HASFILE; | ||||
| 	    break; | ||||
|         case 'E': | ||||
|             r->rule_flags |= RULE_HASFILE; | ||||
|             break; | ||||
|         case 's': | ||||
|             r->rule_flags |= RULE_RESTART; | ||||
|             break; | ||||
|  | @ -231,18 +268,18 @@ static int parseline(char *line, struct rule *r, int lineno) | |||
|         case '~': | ||||
|             r->rule_flags |= RULE_INVERSE; | ||||
|             break; | ||||
| 	case '4': | ||||
| 	    r->rule_flags |= RULE_IPV4; | ||||
| 	    break; | ||||
| 	case '6': | ||||
| 	    r->rule_flags |= RULE_IPV6; | ||||
| 	    break; | ||||
| 	case 'G': | ||||
| 	    r->rule_flags |= RULE_RRQ; | ||||
| 	    break; | ||||
| 	case 'P': | ||||
| 	    r->rule_flags |= RULE_WRQ; | ||||
| 	    break; | ||||
|         case '4': | ||||
|             r->rule_flags |= RULE_IPV4; | ||||
|             break; | ||||
|         case '6': | ||||
|             r->rule_flags |= RULE_IPV6; | ||||
|             break; | ||||
|         case 'G': | ||||
|             r->rule_flags |= RULE_RRQ; | ||||
|             break; | ||||
|         case 'P': | ||||
|             r->rule_flags |= RULE_WRQ; | ||||
|             break; | ||||
|         default: | ||||
|             syslog(LOG_ERR, | ||||
|                    "Remap command \"%s\" on line %d contains invalid char \"%c\"", | ||||
|  | @ -253,20 +290,20 @@ static int parseline(char *line, struct rule *r, int lineno) | |||
|     } | ||||
| 
 | ||||
|     if (r->rule_flags & RULE_REWRITE) { | ||||
| 	    if (r->rule_flags & RULE_INVERSE) { | ||||
| 		syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", | ||||
| 		       lineno, line); | ||||
| 		return -1;              /* Error */ | ||||
| 	    } | ||||
| 	    if ((r->rule_flags & (RULE_GLOBAL|RULE_HASFILE)) | ||||
| 		== (RULE_GLOBAL|RULE_HASFILE)) { | ||||
| 		syslog(LOG_ERR, "E rules cannot be combined with g, line %d: %s\n", | ||||
| 		       lineno, line); | ||||
| 		return -1;              /* Error */ | ||||
| 	    } | ||||
|         if (r->rule_flags & RULE_INVERSE) { | ||||
|             syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", | ||||
|                    lineno, line); | ||||
|             return -1;              /* Error */ | ||||
|         } | ||||
|         if ((r->rule_flags & (RULE_GLOBAL|RULE_SEDG|RULE_HASFILE)) | ||||
|             == (RULE_GLOBAL|RULE_HASFILE)) { | ||||
|             syslog(LOG_ERR, "E rules cannot be combined with g (but gg is OK), line %d: %s\n", | ||||
|                    lineno, line); | ||||
|             return -1;              /* Error */ | ||||
|         } | ||||
|     } else { | ||||
| 	/* RULE_GLOBAL is meaningless without RULE_REWRITE */ | ||||
| 	r->rule_flags &= ~RULE_GLOBAL; | ||||
|         /* RULE_GLOBAL and RULE_SEDG are meaningless without RULE_REWRITE */ | ||||
|         r->rule_flags &= ~(RULE_GLOBAL|RULE_SEDG); | ||||
|     } | ||||
| 
 | ||||
|     /* Read and compile the regex */ | ||||
|  | @ -348,19 +385,23 @@ void freerules(struct rule *r) | |||
| 
 | ||||
| /* Execute a rule set on a string; returns a malloc'd new string. */ | ||||
| char *rewrite_string(const struct formats *pf, | ||||
| 		     const char *input, const struct rule *rules, | ||||
|                      const char *input, const struct rule *rules, | ||||
|                      int mode, int af, match_pattern_callback macrosub, | ||||
|                      const char **errmsg) | ||||
| { | ||||
|     char *current = tfstrdup(input); | ||||
|     char *newstr; | ||||
|     char *newstr, *newerstr; | ||||
|     const char *accerr; | ||||
|     const struct rule *ruleptr = rules; | ||||
|     regmatch_t pmatch[10]; | ||||
|     int len; | ||||
|     int i; | ||||
|     int len, newlen; | ||||
|     int was_match = 0; | ||||
|     int deadman = DEADMAN_MAX_STEPS; | ||||
|     int matchsense; | ||||
|     int pmatches; | ||||
|     unsigned int bad_flags; | ||||
|     int ggoffset; | ||||
| 
 | ||||
|     /* Default error */ | ||||
|     *errmsg = "Remap table failure"; | ||||
|  | @ -376,30 +417,25 @@ char *rewrite_string(const struct formats *pf, | |||
|     if (af != AF_INET6) bad_flags |= RULE_IPV6; | ||||
| 
 | ||||
|     for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { | ||||
| 	if (ruleptr->rule_flags & bad_flags) | ||||
| 	    continue;		/* This rule is excluded by flags */ | ||||
|         if (ruleptr->rule_flags & bad_flags) | ||||
|             continue;		/* This rule is excluded by flags */ | ||||
| 
 | ||||
|         if (!deadman--) { | ||||
|             syslog(LOG_WARNING, | ||||
|                    "remap: Breaking loop, input = %s, last = %s", input, | ||||
|                    current); | ||||
|             free(current); | ||||
|             return NULL;        /* Did not terminate! */ | ||||
|         } | ||||
|         matchsense = ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0; | ||||
|         pmatches = ruleptr->rule_flags & RULE_INVERSE ? 0 : 10; | ||||
| 
 | ||||
|         /* Clear the pmatch[] array */ | ||||
|         for (i = 0; i < 10; i++) | ||||
|             pmatch[i].rm_so = pmatch[i].rm_eo = -1; | ||||
| 
 | ||||
|         do { | ||||
|             if (regexec(&ruleptr->rx, current, 10, pmatch, 0) == | ||||
|                 (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0)) { | ||||
|             if (!deadman--) | ||||
|                 goto dead; | ||||
| 
 | ||||
|             if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0) | ||||
|                 == matchsense) { | ||||
|                 /* Match on this rule */ | ||||
|                 was_match = 1; | ||||
| 
 | ||||
|                 if (ruleptr->rule_flags & RULE_INVERSE) { | ||||
|                     /* No actual match, so clear out the pmatch array */ | ||||
|                     int i; | ||||
|                     for (i = 0; i < 10; i++) | ||||
|                         pmatch[i].rm_so = pmatch[i].rm_eo = -1; | ||||
|                 } | ||||
| 
 | ||||
|                 if (ruleptr->rule_flags & RULE_ABORT) { | ||||
|                     if (verbosity >= 3) { | ||||
|                         syslog(LOG_INFO, "remap: rule %d: abort: %s", | ||||
|  | @ -407,12 +443,8 @@ char *rewrite_string(const struct formats *pf, | |||
|                     } | ||||
|                     if (ruleptr->pattern[0]) { | ||||
|                         /* Custom error message */ | ||||
|                         len = | ||||
|                             genmatchstring(NULL, ruleptr->pattern, current, | ||||
|                                            pmatch, macrosub); | ||||
|                         newstr = tfmalloc(len + 1); | ||||
|                         genmatchstring(newstr, ruleptr->pattern, current, | ||||
|                                        pmatch, macrosub); | ||||
|                         genmatchstring(&newstr, ruleptr->pattern, current, | ||||
|                                        pmatch, macrosub, 0, NULL); | ||||
|                         *errmsg = newstr; | ||||
|                     } else { | ||||
|                         *errmsg = NULL; | ||||
|  | @ -422,58 +454,76 @@ char *rewrite_string(const struct formats *pf, | |||
|                 } | ||||
| 
 | ||||
|                 if (ruleptr->rule_flags & RULE_REWRITE) { | ||||
|                     len = genmatchstring(NULL, ruleptr->pattern, current, | ||||
|                                          pmatch, macrosub); | ||||
|                     newstr = tfmalloc(len + 1); | ||||
|                     genmatchstring(newstr, ruleptr->pattern, current, | ||||
|                                    pmatch, macrosub); | ||||
| 		    if ((ruleptr->rule_flags & RULE_HASFILE) && | ||||
| 			pf->f_validate(newstr, mode, pf, &accerr)) { | ||||
| 			    if (verbosity >= 3) { | ||||
| 				syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", | ||||
| 				       ruleptr->nrule, accerr, newstr); | ||||
| 			    } | ||||
| 			    free(newstr); | ||||
| 			    was_match = 0; | ||||
| 			    break; | ||||
| 			} | ||||
| 		    free(current); | ||||
| 		    current = newstr; | ||||
| 		    if (verbosity >= 3) { | ||||
| 			syslog(LOG_INFO, "remap: rule %d: rewrite: %s", | ||||
| 			       ruleptr->nrule, current); | ||||
| 		    } | ||||
|                 } else if (ruleptr->rule_flags & RULE_HASFILE) { | ||||
| 		    if (pf->f_validate(current, mode, pf, &accerr)) { | ||||
| 			if (verbosity >= 3) { | ||||
| 			    syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", | ||||
| 				   ruleptr->nrule, accerr); | ||||
| 			} | ||||
| 			was_match = 0; | ||||
| 			break; | ||||
| 		    } | ||||
| 		} | ||||
|             } else { | ||||
|                 break;          /* No match, terminate unconditionally */ | ||||
|                     len = genmatchstring(&newstr, ruleptr->pattern, current, | ||||
|                                          pmatch, macrosub, 0, &ggoffset); | ||||
| 
 | ||||
|                     if (ruleptr->rule_flags & RULE_SEDG) { | ||||
|                         /* sed-style partial-matching global */ | ||||
|                         while (ggoffset < len && | ||||
|                                regexec(ruleptr->rx, newstr + ggoffset, | ||||
|                                        pmatches, pmatch, | ||||
|                                        ggoffset ? REG_NOTBOL : 0) | ||||
|                                == matchsense) { | ||||
|                             if (!deadman--) { | ||||
|                                 free(current); | ||||
|                                 current = newstr; | ||||
|                                 goto dead; | ||||
|                             } | ||||
|                             len = genmatchstring(&newerstr, ruleptr->pattern, | ||||
|                                                  newstr, pmatch, macrosub, | ||||
|                                                  ggoffset, &ggoffset); | ||||
|                             free(newstr); | ||||
|                             newstr = newerstr; | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     if ((ruleptr->rule_flags & RULE_HASFILE) && | ||||
|                         pf->f_validate(newstr, mode, pf, &accerr)) { | ||||
|                         if (verbosity >= 3) { | ||||
|                             syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", | ||||
|                                    ruleptr->nrule, accerr, newstr); | ||||
|                         } | ||||
|                         free(newstr); | ||||
|                         was_match = 0; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 free(current); | ||||
|                 current = newstr; | ||||
|                 if (verbosity >= 3) { | ||||
|                     syslog(LOG_INFO, "remap: rule %d: rewrite: %s", | ||||
|                            ruleptr->nrule, current); | ||||
|                 } | ||||
|             } else if (ruleptr->rule_flags & RULE_HASFILE) { | ||||
|                 if (pf->f_validate(current, mode, pf, &accerr)) { | ||||
|                     if (verbosity >= 3) { | ||||
|                         syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", | ||||
|                                ruleptr->nrule, accerr); | ||||
|                     } | ||||
|                     was_match = 0; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             /* If the rule is global, keep going until no match */ | ||||
|         } while (ruleptr->rule_flags & RULE_GLOBAL); | ||||
|         } else { | ||||
|             break;          /* No match, terminate unconditionally */ | ||||
|         } | ||||
|         /* If the rule is (old-style) global, keep going until no match */ | ||||
|     } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); | ||||
| 
 | ||||
|         if (was_match) { | ||||
|             was_match = 0; | ||||
|     if (was_match) { | ||||
|         was_match = 0; | ||||
| 
 | ||||
|             if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { | ||||
|                 if (verbosity >= 3) { | ||||
|                     syslog(LOG_INFO, "remap: rule %d: exit", | ||||
|                            ruleptr->nrule); | ||||
|                 } | ||||
|                 return current; /* Exit here, we're done */ | ||||
|             } else if (ruleptr->rule_flags & RULE_RESTART) { | ||||
|                 ruleptr = rules;        /* Start from the top */ | ||||
|                 if (verbosity >= 3) { | ||||
|                     syslog(LOG_INFO, "remap: rule %d: restart", | ||||
|                            ruleptr->nrule); | ||||
|                 } | ||||
|         if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { | ||||
|             if (verbosity >= 3) { | ||||
|                 syslog(LOG_INFO, "remap: rule %d: exit", | ||||
|                        ruleptr->nrule); | ||||
|             } | ||||
|             return current; /* Exit here, we're done */ | ||||
|         } else if (ruleptr->rule_flags & RULE_RESTART) { | ||||
|             ruleptr = rules;        /* Start from the top */ | ||||
|             if (verbosity >= 3) { | ||||
|                 syslog(LOG_INFO, "remap: rule %d: restart", | ||||
|                        ruleptr->nrule); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -482,4 +532,11 @@ char *rewrite_string(const struct formats *pf, | |||
|         syslog(LOG_INFO, "remap: done"); | ||||
|     } | ||||
|     return current; | ||||
| 
 | ||||
| dead:                           /* Deadman expired */ | ||||
|     syslog(LOG_WARNING, | ||||
|            "remap: Breaking loop, input = %s, last = %s", input, | ||||
|            current); | ||||
|     free(current); | ||||
|     return NULL;        /* Did not terminate! */ | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 H. Peter Anvin
						H. Peter Anvin