Re: unwind and split-horizon DNS
On Sat, Nov 30, 2019 at 08:39:36AM +0100, Otto Moerbeek wrote: > On Fri, Nov 29, 2019 at 11:37:40PM +0100, Björn Ketelaars wrote: > > > On Fri 29/11/2019 21:35, Otto Moerbeek wrote: > > > On Fri, Nov 29, 2019 at 10:27:57AM +0100, Florian Obser wrote: > > > > > > > On Fri, Nov 29, 2019 at 07:28:20AM +0100, Otto Moerbeek wrote: > > > > > On Fri, Nov 29, 2019 at 07:02:27AM +0100, Björn Ketelaars wrote: > > > > > > I experienced no regression while using the free wifi service of the > > > > > > Dutch railways, which is known to do strange things with DNS. > > > > > > > > > > Thanks for testing. The Dutch railways have been a great inspiration > > > > > to unwind work, as florian@ can telll you :-) > > > > > > > > They have got to be good at *something*. Not sure if it's their core > > > > business to annoy the hell out of me, but hey... > > > > > > > > Only joking, overall I'm quite happy with the Dutch railway. I use > > > > them every work day and they get me where I need to go most of the > > > > time. > > > > > > > > -- > > > > I'm not entirely sure you are real. > > > > > > > > > > And here's a rebased diff for your convenience, > > > > The rebased diff results in a different behaviour than the first diff. > > More precise, 'force acceptbogus forwarder' is not respected any more > > resulting in issues with DNSSEC. > > > > I compared the old- and the rebased diff and noticed that some bits have > > been left out. Functionality is restored after applying the diff below. > > > > > > diff --git sbin/unwind/unwind.c sbin/unwind/unwind.c > > index 5a97dcccec4..4687a7cc122 100644 > > --- sbin/unwind/unwind.c > > +++ sbin/unwind/unwind.c > > @@ -675,6 +675,12 @@ merge_config(struct uw_conf *conf, struct uw_conf > > *xconf) > > uw_forwarder, entry); > > } > > > > + for (n = RB_MIN(force_tree, >force); n != NULL; n = nxt) { > > + nxt = RB_NEXT(force_tree, >force, n); > > + RB_REMOVE(force_tree, >force, n); > > + RB_INSERT(force_tree, >force, n); > > + } > > + > > free(xconf); > > } > > > > Thanks for spottting that. I did myself as well, but sent the wrong > version... Below the full corrected diff. Diff has been committed with one grammar change: acceptbogus is now two words. Thanks to the testers, -Otto
Re: unwind and split-horizon DNS
On Fri, Nov 29, 2019 at 11:37:40PM +0100, Björn Ketelaars wrote: > On Fri 29/11/2019 21:35, Otto Moerbeek wrote: > > On Fri, Nov 29, 2019 at 10:27:57AM +0100, Florian Obser wrote: > > > > > On Fri, Nov 29, 2019 at 07:28:20AM +0100, Otto Moerbeek wrote: > > > > On Fri, Nov 29, 2019 at 07:02:27AM +0100, Björn Ketelaars wrote: > > > > > I experienced no regression while using the free wifi service of the > > > > > Dutch railways, which is known to do strange things with DNS. > > > > > > > > Thanks for testing. The Dutch railways have been a great inspiration > > > > to unwind work, as florian@ can telll you :-) > > > > > > They have got to be good at *something*. Not sure if it's their core > > > business to annoy the hell out of me, but hey... > > > > > > Only joking, overall I'm quite happy with the Dutch railway. I use > > > them every work day and they get me where I need to go most of the > > > time. > > > > > > -- > > > I'm not entirely sure you are real. > > > > > > > And here's a rebased diff for your convenience, > > The rebased diff results in a different behaviour than the first diff. > More precise, 'force acceptbogus forwarder' is not respected any more > resulting in issues with DNSSEC. > > I compared the old- and the rebased diff and noticed that some bits have > been left out. Functionality is restored after applying the diff below. > > > diff --git sbin/unwind/unwind.c sbin/unwind/unwind.c > index 5a97dcccec4..4687a7cc122 100644 > --- sbin/unwind/unwind.c > +++ sbin/unwind/unwind.c > @@ -675,6 +675,12 @@ merge_config(struct uw_conf *conf, struct uw_conf *xconf) > uw_forwarder, entry); > } > > + for (n = RB_MIN(force_tree, >force); n != NULL; n = nxt) { > + nxt = RB_NEXT(force_tree, >force, n); > + RB_REMOVE(force_tree, >force, n); > + RB_INSERT(force_tree, >force, n); > + } > + > free(xconf); > } > Thanks for spottting that. I did myself as well, but sent the wrong version... Below the full corrected diff. -Otto Index: frontend.c === RCS file: /cvs/src/sbin/unwind/frontend.c,v retrieving revision 1.41 diff -u -p -r1.41 frontend.c --- frontend.c 29 Nov 2019 15:22:02 - 1.41 +++ frontend.c 29 Nov 2019 20:32:43 - @@ -336,6 +336,7 @@ frontend_dispatch_main(int fd, short eve case IMSG_RECONF_BLOCKLIST_FILE: case IMSG_RECONF_FORWARDER: case IMSG_RECONF_DOT_FORWARDER: + case IMSG_RECONF_FORCE: imsg_receive_config(, ); break; case IMSG_RECONF_END: Index: parse.y === RCS file: /cvs/src/sbin/unwind/parse.y,v retrieving revision 1.20 diff -u -p -r1.20 parse.y --- parse.y 28 Nov 2019 10:02:44 - 1.20 +++ parse.y 29 Nov 2019 20:32:43 - @@ -90,8 +90,9 @@ struct sockaddr_storage *host_ip(const c typedef struct { union { - int64_t number; - char*string; + int64_t number; + char*string; + struct force_treeforce; } v; int lineno; } YYSTYPE; @@ -101,12 +102,13 @@ typedef struct { %token INCLUDE ERROR %token FORWARDER DOT PORT %token AUTHENTICATION NAME PREFERENCE RECURSOR DHCP STUB -%token BLOCK LIST LOG +%token BLOCK LIST LOG FORCE ACCEPTBOGUS %token STRING %token NUMBER -%typeyesno port dot prefopt log +%typeyesno port dot prefopt log acceptbogus %typestring authname +%type force_list %% @@ -117,6 +119,7 @@ grammar : /* empty */ | grammar uw_pref '\n' | grammar uw_forwarder '\n' | grammar block_list '\n' + | grammar force '\n' | grammar error '\n'{ file->errors++; } ; @@ -311,6 +314,63 @@ dot: DOT { $$ = DOT; } log: LOG { $$ = 1; } | /* empty */ { $$ = 0; } ; + +force : FORCE acceptbogus prefopt '{' force_list optnl '}' { + struct force_tree_entry *n, *nxt; + int error = 0; + + for (n = RB_MIN(force_tree, &$5); n != NULL; + n = nxt) { + nxt = RB_NEXT(force_tree, >force, n); + n->acceptbogus = $2; + n->type = $3; + RB_REMOVE(force_tree, &$5, n); + if (RB_INSERT(force_tree, >force, + n)) { + yyerror("%s already in an force "
Re: unwind and split-horizon DNS
On Fri 29/11/2019 21:35, Otto Moerbeek wrote: > On Fri, Nov 29, 2019 at 10:27:57AM +0100, Florian Obser wrote: > > > On Fri, Nov 29, 2019 at 07:28:20AM +0100, Otto Moerbeek wrote: > > > On Fri, Nov 29, 2019 at 07:02:27AM +0100, Björn Ketelaars wrote: > > > > I experienced no regression while using the free wifi service of the > > > > Dutch railways, which is known to do strange things with DNS. > > > > > > Thanks for testing. The Dutch railways have been a great inspiration > > > to unwind work, as florian@ can telll you :-) > > > > They have got to be good at *something*. Not sure if it's their core > > business to annoy the hell out of me, but hey... > > > > Only joking, overall I'm quite happy with the Dutch railway. I use > > them every work day and they get me where I need to go most of the > > time. > > > > -- > > I'm not entirely sure you are real. > > > > And here's a rebased diff for your convenience, The rebased diff results in a different behaviour than the first diff. More precise, 'force acceptbogus forwarder' is not respected any more resulting in issues with DNSSEC. I compared the old- and the rebased diff and noticed that some bits have been left out. Functionality is restored after applying the diff below. diff --git sbin/unwind/unwind.c sbin/unwind/unwind.c index 5a97dcccec4..4687a7cc122 100644 --- sbin/unwind/unwind.c +++ sbin/unwind/unwind.c @@ -675,6 +675,12 @@ merge_config(struct uw_conf *conf, struct uw_conf *xconf) uw_forwarder, entry); } + for (n = RB_MIN(force_tree, >force); n != NULL; n = nxt) { + nxt = RB_NEXT(force_tree, >force, n); + RB_REMOVE(force_tree, >force, n); + RB_INSERT(force_tree, >force, n); + } + free(xconf); }
Re: unwind and split-horizon DNS
On Fri, Nov 29, 2019 at 10:27:57AM +0100, Florian Obser wrote: > On Fri, Nov 29, 2019 at 07:28:20AM +0100, Otto Moerbeek wrote: > > On Fri, Nov 29, 2019 at 07:02:27AM +0100, Björn Ketelaars wrote: > > > I experienced no regression while using the free wifi service of the > > > Dutch railways, which is known to do strange things with DNS. > > > > Thanks for testing. The Dutch railways have been a great inspiration > > to unwind work, as florian@ can telll you :-) > > They have got to be good at *something*. Not sure if it's their core > business to annoy the hell out of me, but hey... > > Only joking, overall I'm quite happy with the Dutch railway. I use > them every work day and they get me where I need to go most of the > time. > > -- > I'm not entirely sure you are real. > And here's a rebased diff for your convenience, -Otto Index: frontend.c === RCS file: /cvs/src/sbin/unwind/frontend.c,v retrieving revision 1.41 diff -u -p -r1.41 frontend.c --- frontend.c 29 Nov 2019 15:22:02 - 1.41 +++ frontend.c 29 Nov 2019 20:30:19 - @@ -336,6 +336,7 @@ frontend_dispatch_main(int fd, short eve case IMSG_RECONF_BLOCKLIST_FILE: case IMSG_RECONF_FORWARDER: case IMSG_RECONF_DOT_FORWARDER: + case IMSG_RECONF_FORCE: imsg_receive_config(, ); break; case IMSG_RECONF_END: Index: parse.y === RCS file: /cvs/src/sbin/unwind/parse.y,v retrieving revision 1.20 diff -u -p -r1.20 parse.y --- parse.y 28 Nov 2019 10:02:44 - 1.20 +++ parse.y 29 Nov 2019 20:30:19 - @@ -90,8 +90,9 @@ struct sockaddr_storage *host_ip(const c typedef struct { union { - int64_t number; - char*string; + int64_t number; + char*string; + struct force_treeforce; } v; int lineno; } YYSTYPE; @@ -101,12 +102,13 @@ typedef struct { %token INCLUDE ERROR %token FORWARDER DOT PORT %token AUTHENTICATION NAME PREFERENCE RECURSOR DHCP STUB -%token BLOCK LIST LOG +%token BLOCK LIST LOG FORCE ACCEPTBOGUS %token STRING %token NUMBER -%typeyesno port dot prefopt log +%typeyesno port dot prefopt log acceptbogus %typestring authname +%type force_list %% @@ -117,6 +119,7 @@ grammar : /* empty */ | grammar uw_pref '\n' | grammar uw_forwarder '\n' | grammar block_list '\n' + | grammar force '\n' | grammar error '\n'{ file->errors++; } ; @@ -311,6 +314,63 @@ dot: DOT { $$ = DOT; } log: LOG { $$ = 1; } | /* empty */ { $$ = 0; } ; + +force : FORCE acceptbogus prefopt '{' force_list optnl '}' { + struct force_tree_entry *n, *nxt; + int error = 0; + + for (n = RB_MIN(force_tree, &$5); n != NULL; + n = nxt) { + nxt = RB_NEXT(force_tree, >force, n); + n->acceptbogus = $2; + n->type = $3; + RB_REMOVE(force_tree, &$5, n); + if (RB_INSERT(force_tree, >force, + n)) { + yyerror("%s already in an force " + "list", n->domain); + error = 1; + } + } + if (error) + YYERROR; + } + ; + +acceptbogus: ACCEPTBOGUS { $$ = 1; } + | /* empty */ { $$ = 0; } + ; + +force_list:force_list optnl STRING { + struct force_tree_entry *e; + size_t len; + + len = strlen($3); + e = malloc(sizeof(*e)); + if (e == NULL) + err(1, NULL); + if (strlcpy(e->domain, $3, sizeof(e->domain)) >= + sizeof(e->domain)) { + yyerror("force %s too long", $3); + free($3); + YYERROR; + } + free($3); + if (len == 0 || e->domain[len-1] != '.') { + if (strlcat(e->domain, ".", +
Re: unwind and split-horizon DNS
On Fri, Nov 29, 2019 at 07:28:20AM +0100, Otto Moerbeek wrote: > On Fri, Nov 29, 2019 at 07:02:27AM +0100, Björn Ketelaars wrote: > > I experienced no regression while using the free wifi service of the > > Dutch railways, which is known to do strange things with DNS. > > Thanks for testing. The Dutch railways have been a great inspiration > to unwind work, as florian@ can telll you :-) They have got to be good at *something*. Not sure if it's their core business to annoy the hell out of me, but hey... Only joking, overall I'm quite happy with the Dutch railway. I use them every work day and they get me where I need to go most of the time. -- I'm not entirely sure you are real.
Re: unwind and split-horizon DNS
On Fri, Nov 29, 2019 at 07:02:27AM +0100, Björn Ketelaars wrote: > On Thu 28/11/2019 16:16, Otto Moerbeek wrote: > > On Thu, Nov 28, 2019 at 03:26:34PM +0100, Otto Moerbeek wrote: > > > > > Hi, > > > > > > In many offices, split horizon DNS is used. This means that if you are > > > in the office you are supposed to use a specific resolver that will > > > hand out different results than when asking for the same name on the > > > rest of the internet. > > > > > > Until now unwind could not really handle that, e.g. in recursing mode, > > > it would produce the view as from outside of the office. > > > > > > With this diff, it becomes possible to force using a specific resolver > > > when resolving names in specific domains. > > > > > > For example, with this unwind.conf: > > > > > > # Office forwarder > > > forwarder 1.2.3.4 > > > force forwarder { > > > myoffice.com > > > dmz.colocation.com > > > } > > > > > > This will make unwind always use the mentioned forwarder for anything > > > under office.com or dmz.colocation.com. If the forwarder is dead, > > > regular resolving is done for these names and www.office.com will > > > likely return the external address. > > > > > > Often split-horizon DNS breaks DNSSEC for these specific domains. If > > > that is the case, you can use > > > > > > force acceptbogus forwarder { > > > ... > > > } > > > > > > please test this, > > > > > > -Otto > > > > > > OAIndex: frontend.c > > > > Dont know hwre that OA is comming from. But it confuses patch, making > > it skip first part of the diff. Proper diff below: > > @Home I'm redirecting all DNS requests to a machine with unbound serving > a couple of local-zones. unwind didn't work for me as these local-zones > would not resolve because of DNSSEC. With your diff, and the config > below unwind works perfect. > > forwarder 10.0.0.1 > force acceptbogus forwarder { > lan > } > > I experienced no regression while using the free wifi service of the > Dutch railways, which is known to do strange things with DNS. Thanks for testing. The Dutch railways have been a great inspiration to unwind work, as florian@ can telll you :-) -Otto
Re: unwind and split-horizon DNS
On Thu 28/11/2019 16:16, Otto Moerbeek wrote: > On Thu, Nov 28, 2019 at 03:26:34PM +0100, Otto Moerbeek wrote: > > > Hi, > > > > In many offices, split horizon DNS is used. This means that if you are > > in the office you are supposed to use a specific resolver that will > > hand out different results than when asking for the same name on the > > rest of the internet. > > > > Until now unwind could not really handle that, e.g. in recursing mode, > > it would produce the view as from outside of the office. > > > > With this diff, it becomes possible to force using a specific resolver > > when resolving names in specific domains. > > > > For example, with this unwind.conf: > > > > # Office forwarder > > forwarder 1.2.3.4 > > force forwarder { > > myoffice.com > > dmz.colocation.com > > } > > > > This will make unwind always use the mentioned forwarder for anything > > under office.com or dmz.colocation.com. If the forwarder is dead, > > regular resolving is done for these names and www.office.com will > > likely return the external address. > > > > Often split-horizon DNS breaks DNSSEC for these specific domains. If > > that is the case, you can use > > > > force acceptbogus forwarder { > > ... > > } > > > > please test this, > > > > -Otto > > > > OAIndex: frontend.c > > Dont know hwre that OA is comming from. But it confuses patch, making > it skip first part of the diff. Proper diff below: @Home I'm redirecting all DNS requests to a machine with unbound serving a couple of local-zones. unwind didn't work for me as these local-zones would not resolve because of DNSSEC. With your diff, and the config below unwind works perfect. forwarder 10.0.0.1 force acceptbogus forwarder { lan } I experienced no regression while using the free wifi service of the Dutch railways, which is known to do strange things with DNS.
Re: unwind and split-horizon DNS
On Thu, Nov 28, 2019 at 03:26:34PM +0100, Otto Moerbeek wrote: > Hi, > > In many offices, split horizon DNS is used. This means that if you are > in the office you are supposed to use a specific resolver that will > hand out different results than when asking for the same name on the > rest of the internet. > > Until now unwind could not really handle that, e.g. in recursing mode, > it would produce the view as from outside of the office. > > With this diff, it becomes possible to force using a specific resolver > when resolving names in specific domains. > > For example, with this unwind.conf: > > # Office forwarder > forwarder 1.2.3.4 > force forwarder { > myoffice.com > dmz.colocation.com > } > > This will make unwind always use the mentioned forwarder for anything > under office.com or dmz.colocation.com. If the forwarder is dead, > regular resolving is done for these names and www.office.com will > likely return the external address. > > Often split-horizon DNS breaks DNSSEC for these specific domains. If > that is the case, you can use > > force acceptbogus forwarder { > ... > } > > please test this, > > -Otto > > OAIndex: frontend.c Dont know hwre that OA is comming from. But it confuses patch, making it skip first part of the diff. Proper diff below: -Otto Index: frontend.c === RCS file: /cvs/src/sbin/unwind/frontend.c,v retrieving revision 1.40 diff -u -p -r1.40 frontend.c --- frontend.c 27 Nov 2019 17:09:12 - 1.40 +++ frontend.c 28 Nov 2019 14:24:17 - @@ -336,6 +336,7 @@ frontend_dispatch_main(int fd, short eve case IMSG_RECONF_BLOCKLIST_FILE: case IMSG_RECONF_FORWARDER: case IMSG_RECONF_DOT_FORWARDER: + case IMSG_RECONF_FORCE: imsg_receive_config(, ); break; case IMSG_RECONF_END: Index: parse.y === RCS file: /cvs/src/sbin/unwind/parse.y,v retrieving revision 1.20 diff -u -p -r1.20 parse.y --- parse.y 28 Nov 2019 10:02:44 - 1.20 +++ parse.y 28 Nov 2019 14:24:17 - @@ -90,8 +90,9 @@ struct sockaddr_storage *host_ip(const c typedef struct { union { - int64_t number; - char*string; + int64_t number; + char*string; + struct force_treeforce; } v; int lineno; } YYSTYPE; @@ -101,12 +102,13 @@ typedef struct { %token INCLUDE ERROR %token FORWARDER DOT PORT %token AUTHENTICATION NAME PREFERENCE RECURSOR DHCP STUB -%token BLOCK LIST LOG +%token BLOCK LIST LOG FORCE ACCEPTBOGUS %token STRING %token NUMBER -%typeyesno port dot prefopt log +%typeyesno port dot prefopt log acceptbogus %typestring authname +%type force_list %% @@ -117,6 +119,7 @@ grammar : /* empty */ | grammar uw_pref '\n' | grammar uw_forwarder '\n' | grammar block_list '\n' + | grammar force '\n' | grammar error '\n'{ file->errors++; } ; @@ -311,6 +314,63 @@ dot: DOT { $$ = DOT; } log: LOG { $$ = 1; } | /* empty */ { $$ = 0; } ; + +force : FORCE acceptbogus prefopt '{' force_list optnl '}' { + struct force_tree_entry *n, *nxt; + int error = 0; + + for (n = RB_MIN(force_tree, &$5); n != NULL; + n = nxt) { + nxt = RB_NEXT(force_tree, >force, n); + n->acceptbogus = $2; + n->type = $3; + RB_REMOVE(force_tree, &$5, n); + if (RB_INSERT(force_tree, >force, + n)) { + yyerror("%s already in an force " + "list", n->domain); + error = 1; + } + } + if (error) + YYERROR; + } + ; + +acceptbogus: ACCEPTBOGUS { $$ = 1; } + | /* empty */ { $$ = 0; } + ; + +force_list:force_list optnl STRING { + struct force_tree_entry *e; + size_t len; + + len = strlen($3); + e = malloc(sizeof(*e)); + if (e == NULL) + err(1, NULL); +
unwind and split-horizon DNS
Hi, In many offices, split horizon DNS is used. This means that if you are in the office you are supposed to use a specific resolver that will hand out different results than when asking for the same name on the rest of the internet. Until now unwind could not really handle that, e.g. in recursing mode, it would produce the view as from outside of the office. With this diff, it becomes possible to force using a specific resolver when resolving names in specific domains. For example, with this unwind.conf: # Office forwarder forwarder 1.2.3.4 force forwarder { myoffice.com dmz.colocation.com } This will make unwind always use the mentioned forwarder for anything under office.com or dmz.colocation.com. If the forwarder is dead, regular resolving is done for these names and www.office.com will likely return the external address. Often split-horizon DNS breaks DNSSEC for these specific domains. If that is the case, you can use force acceptbogus forwarder { ... } please test this, -Otto OAIndex: frontend.c === RCS file: /cvs/src/sbin/unwind/frontend.c,v retrieving revision 1.40 diff -u -p -r1.40 frontend.c --- frontend.c 27 Nov 2019 17:09:12 - 1.40 +++ frontend.c 28 Nov 2019 14:24:17 - @@ -336,6 +336,7 @@ frontend_dispatch_main(int fd, short eve case IMSG_RECONF_BLOCKLIST_FILE: case IMSG_RECONF_FORWARDER: case IMSG_RECONF_DOT_FORWARDER: + case IMSG_RECONF_FORCE: imsg_receive_config(, ); break; case IMSG_RECONF_END: Index: parse.y === RCS file: /cvs/src/sbin/unwind/parse.y,v retrieving revision 1.20 diff -u -p -r1.20 parse.y --- parse.y 28 Nov 2019 10:02:44 - 1.20 +++ parse.y 28 Nov 2019 14:24:17 - @@ -90,8 +90,9 @@ struct sockaddr_storage *host_ip(const c typedef struct { union { - int64_t number; - char*string; + int64_t number; + char*string; + struct force_treeforce; } v; int lineno; } YYSTYPE; @@ -101,12 +102,13 @@ typedef struct { %token INCLUDE ERROR %token FORWARDER DOT PORT %token AUTHENTICATION NAME PREFERENCE RECURSOR DHCP STUB -%token BLOCK LIST LOG +%token BLOCK LIST LOG FORCE ACCEPTBOGUS %token STRING %token NUMBER -%typeyesno port dot prefopt log +%typeyesno port dot prefopt log acceptbogus %typestring authname +%type force_list %% @@ -117,6 +119,7 @@ grammar : /* empty */ | grammar uw_pref '\n' | grammar uw_forwarder '\n' | grammar block_list '\n' + | grammar force '\n' | grammar error '\n'{ file->errors++; } ; @@ -311,6 +314,63 @@ dot: DOT { $$ = DOT; } log: LOG { $$ = 1; } | /* empty */ { $$ = 0; } ; + +force : FORCE acceptbogus prefopt '{' force_list optnl '}' { + struct force_tree_entry *n, *nxt; + int error = 0; + + for (n = RB_MIN(force_tree, &$5); n != NULL; + n = nxt) { + nxt = RB_NEXT(force_tree, >force, n); + n->acceptbogus = $2; + n->type = $3; + RB_REMOVE(force_tree, &$5, n); + if (RB_INSERT(force_tree, >force, + n)) { + yyerror("%s already in an force " + "list", n->domain); + error = 1; + } + } + if (error) + YYERROR; + } + ; + +acceptbogus: ACCEPTBOGUS { $$ = 1; } + | /* empty */ { $$ = 0; } + ; + +force_list:force_list optnl STRING { + struct force_tree_entry *e; + size_t len; + + len = strlen($3); + e = malloc(sizeof(*e)); + if (e == NULL) + err(1, NULL); + if (strlcpy(e->domain, $3, sizeof(e->domain)) >= + sizeof(e->domain)) { + yyerror("force %s too long", $3); + free($3); + YYERROR; + } +