]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/libalias/alias_proxy.c
This commit was generated by cvs2svn to compensate for changes in r147466,
[FreeBSD/FreeBSD.git] / sys / netinet / libalias / alias_proxy.c
1 /*-
2  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 /* file: alias_proxy.c
31
32     This file encapsulates special operations related to transparent
33     proxy redirection.  This is where packets with a particular destination,
34     usually tcp port 80, are redirected to a proxy server.
35
36     When packets are proxied, the destination address and port are
37     modified.  In certain cases, it is necessary to somehow encode
38     the original address/port info into the packet.  Two methods are
39     presently supported: addition of a [DEST addr port] string at the
40     beginning of a tcp stream, or inclusion of an optional field
41     in the IP header.
42
43     There is one public API function:
44
45         PacketAliasProxyRule()    -- Adds and deletes proxy
46                                      rules.
47
48     Rules are stored in a linear linked list, so lookup efficiency
49     won't be too good for large lists.
50
51
52     Initial development: April, 1998 (cjm)
53 */
54
55
56 /* System includes */
57 #ifdef _KERNEL
58 #include <sys/param.h>
59 #include <sys/ctype.h>
60 #include <sys/libkern.h>
61 #include <sys/kernel.h>
62 #include <sys/malloc.h>
63 #include <sys/limits.h>
64 #else
65 #include <sys/types.h>
66 #include <sys/socket.h>
67 #include <ctype.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <netdb.h>
72 #include <arpa/inet.h>
73 #endif
74
75 /* BSD IPV4 includes */
76 #include <netinet/in_systm.h>
77 #include <netinet/in.h>
78 #include <netinet/ip.h>
79 #include <netinet/tcp.h>
80
81 #ifdef _KERNEL
82 #include <netinet/libalias/alias.h>
83 #include <netinet/libalias/alias_local.h>
84 #else
85 #include "alias.h"              /* Public API functions for libalias */
86 #include "alias_local.h"        /* Functions used by alias*.c */
87 #endif
88
89 /*
90     Data structures
91  */
92
93 /*
94  * A linked list of arbitrary length, based on struct proxy_entry is
95  * used to store proxy rules.
96  */
97 struct proxy_entry {
98         struct libalias *la;
99 #define PROXY_TYPE_ENCODE_NONE      1
100 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
101 #define PROXY_TYPE_ENCODE_IPHDR     3
102         int             rule_index;
103         int             proxy_type;
104         u_char          proto;
105         u_short         proxy_port;
106         u_short         server_port;
107
108         struct in_addr  server_addr;
109
110         struct in_addr  src_addr;
111         struct in_addr  src_mask;
112
113         struct in_addr  dst_addr;
114         struct in_addr  dst_mask;
115
116         struct proxy_entry *next;
117         struct proxy_entry *last;
118 };
119
120
121
122 /*
123     File scope variables
124 */
125
126
127
128 /* Local (static) functions:
129
130     IpMask()                 -- Utility function for creating IP
131                                 masks from integer (1-32) specification.
132     IpAddr()                 -- Utility function for converting string
133                                 to IP address
134     IpPort()                 -- Utility function for converting string
135                                 to port number
136     RuleAdd()                -- Adds an element to the rule list.
137     RuleDelete()             -- Removes an element from the rule list.
138     RuleNumberDelete()       -- Removes all elements from the rule list
139                                 having a certain rule number.
140     ProxyEncodeTcpStream()   -- Adds [DEST x.x.x.x xxxx] to the beginning
141                                 of a TCP stream.
142     ProxyEncodeIpHeader()    -- Adds an IP option indicating the true
143                                 destination of a proxied IP packet
144 */
145
146 #ifdef  _KERNEL         /* XXX: can it be moved to libkern? */
147 static int inet_aton(const char *cp, struct in_addr *addr);
148 #endif
149 static int      IpMask(int, struct in_addr *);
150 static int      IpAddr(char *, struct in_addr *);
151 static int      IpPort(char *, int, int *);
152 static void     RuleAdd(struct libalias *la, struct proxy_entry *);
153 static void     RuleDelete(struct proxy_entry *);
154 static int      RuleNumberDelete(struct libalias *la, int);
155 static void     ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
156 static void     ProxyEncodeIpHeader(struct ip *, int);
157
158 #ifdef  _KERNEL
159 static int
160 inet_aton(cp, addr)
161         const char *cp;
162         struct in_addr *addr;
163 {
164         u_long parts[4];
165         in_addr_t val;
166         const char *c;
167         char *endptr;
168         int gotend, n;
169
170         c = (const char *)cp;
171         n = 0;
172         /*
173          * Run through the string, grabbing numbers until
174          * the end of the string, or some error
175          */
176         gotend = 0;
177         while (!gotend) {
178                 unsigned long l;
179
180                 l = strtoul(c, &endptr, 0);
181
182                 if (l == ULONG_MAX || l == 0)
183                         return (0);
184
185                 val = (in_addr_t)l;     
186                 /* 
187                  * If the whole string is invalid, endptr will equal
188                  * c.. this way we can make sure someone hasn't
189                  * gone '.12' or something which would get past
190                  * the next check.
191                  */
192                 if (endptr == c)
193                         return (0);
194                 parts[n] = val;
195                 c = endptr;
196
197                 /* Check the next character past the previous number's end */
198                 switch (*c) {
199                 case '.' :
200                         /* Make sure we only do 3 dots .. */
201                         if (n == 3)     /* Whoops. Quit. */
202                                 return (0);
203                         n++;
204                         c++;
205                         break;
206
207                 case '\0':
208                         gotend = 1;
209                         break;
210
211                 default:
212                         if (isspace((unsigned char)*c)) {
213                                 gotend = 1;
214                                 break;
215                         } else
216                                 return (0);     /* Invalid character, so fail */
217                 }
218
219         }
220
221         /*
222          * Concoct the address according to
223          * the number of parts specified.
224          */
225
226         switch (n) {
227         case 0:                         /* a -- 32 bits */
228                 /*
229                  * Nothing is necessary here.  Overflow checking was
230                  * already done in strtoul().
231                  */
232                 break;
233         case 1:                         /* a.b -- 8.24 bits */
234                 if (val > 0xffffff || parts[0] > 0xff)
235                         return (0);
236                 val |= parts[0] << 24;
237                 break;
238
239         case 2:                         /* a.b.c -- 8.8.16 bits */
240                 if (val > 0xffff || parts[0] > 0xff || parts[1] > 0xff)
241                         return (0);
242                 val |= (parts[0] << 24) | (parts[1] << 16);
243                 break;
244
245         case 3:                         /* a.b.c.d -- 8.8.8.8 bits */
246                 if (val > 0xff || parts[0] > 0xff || parts[1] > 0xff ||
247                     parts[2] > 0xff)
248                         return (0);
249                 val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
250                 break;
251         }
252
253         if (addr != NULL)
254                 addr->s_addr = htonl(val);
255         return (1);
256 }
257 #endif
258
259 static int
260 IpMask(int nbits, struct in_addr *mask)
261 {
262         int i;
263         u_int imask;
264
265         if (nbits < 0 || nbits > 32)
266                 return (-1);
267
268         imask = 0;
269         for (i = 0; i < nbits; i++)
270                 imask = (imask >> 1) + 0x80000000;
271         mask->s_addr = htonl(imask);
272
273         return (0);
274 }
275
276 static int
277 IpAddr(char *s, struct in_addr *addr)
278 {
279         if (inet_aton(s, addr) == 0)
280                 return (-1);
281         else
282                 return (0);
283 }
284
285 static int
286 IpPort(char *s, int proto, int *port)
287 {
288         int n;
289
290         n = sscanf(s, "%d", port);
291         if (n != 1)
292 #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
293         {
294                 struct servent *se;
295
296                 if (proto == IPPROTO_TCP)
297                         se = getservbyname(s, "tcp");
298                 else if (proto == IPPROTO_UDP)
299                         se = getservbyname(s, "udp");
300                 else
301                         return (-1);
302
303                 if (se == NULL)
304                         return (-1);
305
306                 *port = (u_int) ntohs(se->s_port);
307         }
308 #else
309                 return (-1);
310 #endif
311         return (0);
312 }
313
314 void
315 RuleAdd(struct libalias *la, struct proxy_entry *entry)
316 {
317         int rule_index;
318         struct proxy_entry *ptr;
319         struct proxy_entry *ptr_last;
320
321         if (la->proxyList == NULL) {
322                 la->proxyList = entry;
323                 entry->last = NULL;
324                 entry->next = NULL;
325                 return;
326         }
327         entry->la = la;
328
329         rule_index = entry->rule_index;
330         ptr = la->proxyList;
331         ptr_last = NULL;
332         while (ptr != NULL) {
333                 if (ptr->rule_index >= rule_index) {
334                         if (ptr_last == NULL) {
335                                 entry->next = la->proxyList;
336                                 entry->last = NULL;
337                                 la->proxyList->last = entry;
338                                 la->proxyList = entry;
339                                 return;
340                         }
341                         ptr_last->next = entry;
342                         ptr->last = entry;
343                         entry->last = ptr->last;
344                         entry->next = ptr;
345                         return;
346                 }
347                 ptr_last = ptr;
348                 ptr = ptr->next;
349         }
350
351         ptr_last->next = entry;
352         entry->last = ptr_last;
353         entry->next = NULL;
354 }
355
356 static void
357 RuleDelete(struct proxy_entry *entry)
358 {
359         struct libalias *la;
360
361         la = entry->la;
362         if (entry->last != NULL)
363                 entry->last->next = entry->next;
364         else
365                 la->proxyList = entry->next;
366
367         if (entry->next != NULL)
368                 entry->next->last = entry->last;
369
370         free(entry);
371 }
372
373 static int
374 RuleNumberDelete(struct libalias *la, int rule_index)
375 {
376         int err;
377         struct proxy_entry *ptr;
378
379         err = -1;
380         ptr = la->proxyList;
381         while (ptr != NULL) {
382                 struct proxy_entry *ptr_next;
383
384                 ptr_next = ptr->next;
385                 if (ptr->rule_index == rule_index) {
386                         err = 0;
387                         RuleDelete(ptr);
388                 }
389                 ptr = ptr_next;
390         }
391
392         return (err);
393 }
394
395 static void
396 ProxyEncodeTcpStream(struct alias_link *lnk,
397     struct ip *pip,
398     int maxpacketsize)
399 {
400         int slen;
401         char buffer[40];
402         struct tcphdr *tc;
403
404 /* Compute pointer to tcp header */
405         tc = (struct tcphdr *)ip_next(pip);
406
407 /* Don't modify if once already modified */
408
409         if (GetAckModified(lnk))
410                 return;
411
412 /* Translate destination address and port to string form */
413         snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
414             inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
415
416 /* Pad string out to a multiple of two in length */
417         slen = strlen(buffer);
418         switch (slen % 2) {
419         case 0:
420                 strcat(buffer, " \n");
421                 slen += 2;
422                 break;
423         case 1:
424                 strcat(buffer, "\n");
425                 slen += 1;
426         }
427
428 /* Check for packet overflow */
429         if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
430                 return;
431
432 /* Shift existing TCP data and insert destination string */
433         {
434                 int dlen;
435                 int hlen;
436                 u_char *p;
437
438                 hlen = (pip->ip_hl + tc->th_off) << 2;
439                 dlen = ntohs(pip->ip_len) - hlen;
440
441 /* Modify first packet that has data in it */
442
443                 if (dlen == 0)
444                         return;
445
446                 p = (char *)pip;
447                 p += hlen;
448
449                 bcopy(p, p + slen, dlen);
450                 memcpy(p, buffer, slen);
451         }
452
453 /* Save information about modfied sequence number */
454         {
455                 int delta;
456
457                 SetAckModified(lnk);
458                 delta = GetDeltaSeqOut(pip, lnk);
459                 AddSeq(pip, lnk, delta + slen);
460         }
461
462 /* Update IP header packet length and checksum */
463         {
464                 int accumulate;
465
466                 accumulate = pip->ip_len;
467                 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
468                 accumulate -= pip->ip_len;
469
470                 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
471         }
472
473 /* Update TCP checksum, Use TcpChecksum since so many things have
474    already changed. */
475
476         tc->th_sum = 0;
477         tc->th_sum = TcpChecksum(pip);
478 }
479
480 static void
481 ProxyEncodeIpHeader(struct ip *pip,
482     int maxpacketsize)
483 {
484 #define OPTION_LEN_BYTES  8
485 #define OPTION_LEN_INT16  4
486 #define OPTION_LEN_INT32  2
487         u_char option[OPTION_LEN_BYTES];
488
489 #ifdef LIBALIAS_DEBUG
490         fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
491         fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
492 #endif
493
494         (void)maxpacketsize;
495
496 /* Check to see that there is room to add an IP option */
497         if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
498                 return;
499
500 /* Build option and copy into packet */
501         {
502                 u_char *ptr;
503                 struct tcphdr *tc;
504
505                 ptr = (u_char *) pip;
506                 ptr += 20;
507                 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
508
509                 option[0] = 0x64;       /* class: 3 (reserved), option 4 */
510                 option[1] = OPTION_LEN_BYTES;
511
512                 memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
513
514                 tc = (struct tcphdr *)ip_next(pip);
515                 memcpy(&option[6], (u_char *) & tc->th_sport, 2);
516
517                 memcpy(ptr, option, 8);
518         }
519
520 /* Update checksum, header length and packet length */
521         {
522                 int i;
523                 int accumulate;
524                 u_short *sptr;
525
526                 sptr = (u_short *) option;
527                 accumulate = 0;
528                 for (i = 0; i < OPTION_LEN_INT16; i++)
529                         accumulate -= *(sptr++);
530
531                 sptr = (u_short *) pip;
532                 accumulate += *sptr;
533                 pip->ip_hl += OPTION_LEN_INT32;
534                 accumulate -= *sptr;
535
536                 accumulate += pip->ip_len;
537                 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
538                 accumulate -= pip->ip_len;
539
540                 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
541         }
542 #undef OPTION_LEN_BYTES
543 #undef OPTION_LEN_INT16
544 #undef OPTION_LEN_INT32
545 #ifdef LIBALIAS_DEBUG
546         fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
547         fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
548 #endif
549 }
550
551
552 /* Functions by other packet alias source files
553
554     ProxyCheck()         -- Checks whether an outgoing packet should
555                             be proxied.
556     ProxyModify()        -- Encodes the original destination address/port
557                             for a packet which is to be redirected to
558                             a proxy server.
559 */
560
561 int
562 ProxyCheck(struct libalias *la, struct ip *pip,
563     struct in_addr *proxy_server_addr,
564     u_short * proxy_server_port)
565 {
566         u_short dst_port;
567         struct in_addr src_addr;
568         struct in_addr dst_addr;
569         struct proxy_entry *ptr;
570
571         src_addr = pip->ip_src;
572         dst_addr = pip->ip_dst;
573         dst_port = ((struct tcphdr *)ip_next(pip))
574             ->th_dport;
575
576         ptr = la->proxyList;
577         while (ptr != NULL) {
578                 u_short proxy_port;
579
580                 proxy_port = ptr->proxy_port;
581                 if ((dst_port == proxy_port || proxy_port == 0)
582                     && pip->ip_p == ptr->proto
583                     && src_addr.s_addr != ptr->server_addr.s_addr) {
584                         struct in_addr src_addr_masked;
585                         struct in_addr dst_addr_masked;
586
587                         src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
588                         dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
589
590                         if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
591                             && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
592                                 if ((*proxy_server_port = ptr->server_port) == 0)
593                                         *proxy_server_port = dst_port;
594                                 *proxy_server_addr = ptr->server_addr;
595                                 return (ptr->proxy_type);
596                         }
597                 }
598                 ptr = ptr->next;
599         }
600
601         return (0);
602 }
603
604 void
605 ProxyModify(struct libalias *la, struct alias_link *lnk,
606     struct ip *pip,
607     int maxpacketsize,
608     int proxy_type)
609 {
610
611         (void)la;
612
613         switch (proxy_type) {
614                 case PROXY_TYPE_ENCODE_IPHDR:
615                 ProxyEncodeIpHeader(pip, maxpacketsize);
616                 break;
617
618         case PROXY_TYPE_ENCODE_TCPSTREAM:
619                 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
620                 break;
621         }
622 }
623
624
625 /*
626     Public API functions
627 */
628
629 int
630 LibAliasProxyRule(struct libalias *la, const char *cmd)
631 {
632 /*
633  * This function takes command strings of the form:
634  *
635  *   server <addr>[:<port>]
636  *   [port <port>]
637  *   [rule n]
638  *   [proto tcp|udp]
639  *   [src <addr>[/n]]
640  *   [dst <addr>[/n]]
641  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
642  *
643  *   delete <rule number>
644  *
645  * Subfields can be in arbitrary order.  Port numbers and addresses
646  * must be in either numeric or symbolic form. An optional rule number
647  * is used to control the order in which rules are searched.  If two
648  * rules have the same number, then search order cannot be guaranteed,
649  * and the rules should be disjoint.  If no rule number is specified,
650  * then 0 is used, and group 0 rules are always checked before any
651  * others.
652  */
653         int i, n, len;
654         int cmd_len;
655         int token_count;
656         int state;
657         char *token;
658         char buffer[256];
659         char str_port[sizeof(buffer)];
660         char str_server_port[sizeof(buffer)];
661         char *res = buffer;
662
663         int rule_index;
664         int proto;
665         int proxy_type;
666         int proxy_port;
667         int server_port;
668         struct in_addr server_addr;
669         struct in_addr src_addr, src_mask;
670         struct in_addr dst_addr, dst_mask;
671         struct proxy_entry *proxy_entry;
672
673 /* Copy command line into a buffer */
674         cmd += strspn(cmd, " \t");
675         cmd_len = strlen(cmd);
676         if (cmd_len > (int)(sizeof(buffer) - 1))
677                 return (-1);
678         strcpy(buffer, cmd);
679
680 /* Convert to lower case */
681         len = strlen(buffer);
682         for (i = 0; i < len; i++)
683                 buffer[i] = tolower((unsigned char)buffer[i]);
684
685 /* Set default proxy type */
686
687 /* Set up default values */
688         rule_index = 0;
689         proxy_type = PROXY_TYPE_ENCODE_NONE;
690         proto = IPPROTO_TCP;
691         proxy_port = 0;
692         server_addr.s_addr = 0;
693         server_port = 0;
694         src_addr.s_addr = 0;
695         IpMask(0, &src_mask);
696         dst_addr.s_addr = 0;
697         IpMask(0, &dst_mask);
698
699         str_port[0] = 0;
700         str_server_port[0] = 0;
701
702 /* Parse command string with state machine */
703 #define STATE_READ_KEYWORD    0
704 #define STATE_READ_TYPE       1
705 #define STATE_READ_PORT       2
706 #define STATE_READ_SERVER     3
707 #define STATE_READ_RULE       4
708 #define STATE_READ_DELETE     5
709 #define STATE_READ_PROTO      6
710 #define STATE_READ_SRC        7
711 #define STATE_READ_DST        8
712         state = STATE_READ_KEYWORD;
713         token = strsep(&res, " \t");
714         token_count = 0;
715         while (token != NULL) {
716                 token_count++;
717                 switch (state) {
718                 case STATE_READ_KEYWORD:
719                         if (strcmp(token, "type") == 0)
720                                 state = STATE_READ_TYPE;
721                         else if (strcmp(token, "port") == 0)
722                                 state = STATE_READ_PORT;
723                         else if (strcmp(token, "server") == 0)
724                                 state = STATE_READ_SERVER;
725                         else if (strcmp(token, "rule") == 0)
726                                 state = STATE_READ_RULE;
727                         else if (strcmp(token, "delete") == 0)
728                                 state = STATE_READ_DELETE;
729                         else if (strcmp(token, "proto") == 0)
730                                 state = STATE_READ_PROTO;
731                         else if (strcmp(token, "src") == 0)
732                                 state = STATE_READ_SRC;
733                         else if (strcmp(token, "dst") == 0)
734                                 state = STATE_READ_DST;
735                         else
736                                 return (-1);
737                         break;
738
739                 case STATE_READ_TYPE:
740                         if (strcmp(token, "encode_ip_hdr") == 0)
741                                 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
742                         else if (strcmp(token, "encode_tcp_stream") == 0)
743                                 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
744                         else if (strcmp(token, "no_encode") == 0)
745                                 proxy_type = PROXY_TYPE_ENCODE_NONE;
746                         else
747                                 return (-1);
748                         state = STATE_READ_KEYWORD;
749                         break;
750
751                 case STATE_READ_PORT:
752                         strcpy(str_port, token);
753                         state = STATE_READ_KEYWORD;
754                         break;
755
756                 case STATE_READ_SERVER:
757                         {
758                                 int err;
759                                 char *p;
760                                 char s[sizeof(buffer)];
761
762                                 p = token;
763                                 while (*p != ':' && *p != 0)
764                                         p++;
765
766                                 if (*p != ':') {
767                                         err = IpAddr(token, &server_addr);
768                                         if (err)
769                                                 return (-1);
770                                 } else {
771                                         *p = ' ';
772
773                                         n = sscanf(token, "%s %s", s, str_server_port);
774                                         if (n != 2)
775                                                 return (-1);
776
777                                         err = IpAddr(s, &server_addr);
778                                         if (err)
779                                                 return (-1);
780                                 }
781                         }
782                         state = STATE_READ_KEYWORD;
783                         break;
784
785                 case STATE_READ_RULE:
786                         n = sscanf(token, "%d", &rule_index);
787                         if (n != 1 || rule_index < 0)
788                                 return (-1);
789                         state = STATE_READ_KEYWORD;
790                         break;
791
792                 case STATE_READ_DELETE:
793                         {
794                                 int err;
795                                 int rule_to_delete;
796
797                                 if (token_count != 2)
798                                         return (-1);
799
800                                 n = sscanf(token, "%d", &rule_to_delete);
801                                 if (n != 1)
802                                         return (-1);
803                                 err = RuleNumberDelete(la, rule_to_delete);
804                                 if (err)
805                                         return (-1);
806                                 return (0);
807                         }
808
809                 case STATE_READ_PROTO:
810                         if (strcmp(token, "tcp") == 0)
811                                 proto = IPPROTO_TCP;
812                         else if (strcmp(token, "udp") == 0)
813                                 proto = IPPROTO_UDP;
814                         else
815                                 return (-1);
816                         state = STATE_READ_KEYWORD;
817                         break;
818
819                 case STATE_READ_SRC:
820                 case STATE_READ_DST:
821                         {
822                                 int err;
823                                 char *p;
824                                 struct in_addr mask;
825                                 struct in_addr addr;
826
827                                 p = token;
828                                 while (*p != '/' && *p != 0)
829                                         p++;
830
831                                 if (*p != '/') {
832                                         IpMask(32, &mask);
833                                         err = IpAddr(token, &addr);
834                                         if (err)
835                                                 return (-1);
836                                 } else {
837                                         int nbits;
838                                         char s[sizeof(buffer)];
839
840                                         *p = ' ';
841                                         n = sscanf(token, "%s %d", s, &nbits);
842                                         if (n != 2)
843                                                 return (-1);
844
845                                         err = IpAddr(s, &addr);
846                                         if (err)
847                                                 return (-1);
848
849                                         err = IpMask(nbits, &mask);
850                                         if (err)
851                                                 return (-1);
852                                 }
853
854                                 if (state == STATE_READ_SRC) {
855                                         src_addr = addr;
856                                         src_mask = mask;
857                                 } else {
858                                         dst_addr = addr;
859                                         dst_mask = mask;
860                                 }
861                         }
862                         state = STATE_READ_KEYWORD;
863                         break;
864
865                 default:
866                         return (-1);
867                         break;
868                 }
869
870                 do {
871                         token = strsep(&res, " \t");
872                 } while (token != NULL && !*token);
873         }
874 #undef STATE_READ_KEYWORD
875 #undef STATE_READ_TYPE
876 #undef STATE_READ_PORT
877 #undef STATE_READ_SERVER
878 #undef STATE_READ_RULE
879 #undef STATE_READ_DELETE
880 #undef STATE_READ_PROTO
881 #undef STATE_READ_SRC
882 #undef STATE_READ_DST
883
884 /* Convert port strings to numbers.  This needs to be done after
885    the string is parsed, because the prototype might not be designated
886    before the ports (which might be symbolic entries in /etc/services) */
887
888         if (strlen(str_port) != 0) {
889                 int err;
890
891                 err = IpPort(str_port, proto, &proxy_port);
892                 if (err)
893                         return (-1);
894         } else {
895                 proxy_port = 0;
896         }
897
898         if (strlen(str_server_port) != 0) {
899                 int err;
900
901                 err = IpPort(str_server_port, proto, &server_port);
902                 if (err)
903                         return (-1);
904         } else {
905                 server_port = 0;
906         }
907
908 /* Check that at least the server address has been defined */
909         if (server_addr.s_addr == 0)
910                 return (-1);
911
912 /* Add to linked list */
913         proxy_entry = malloc(sizeof(struct proxy_entry));
914         if (proxy_entry == NULL)
915                 return (-1);
916
917         proxy_entry->proxy_type = proxy_type;
918         proxy_entry->rule_index = rule_index;
919         proxy_entry->proto = proto;
920         proxy_entry->proxy_port = htons(proxy_port);
921         proxy_entry->server_port = htons(server_port);
922         proxy_entry->server_addr = server_addr;
923         proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
924         proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
925         proxy_entry->src_mask = src_mask;
926         proxy_entry->dst_mask = dst_mask;
927
928         RuleAdd(la, proxy_entry);
929
930         return (0);
931 }