]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/libalias/alias_proxy.c
This commit was generated by cvs2svn to compensate for changes in r151600,
[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 #ifdef _KERNEL
478         tc->th_x2 = 1;
479 #else
480         tc->th_sum = TcpChecksum(pip);
481 #endif
482 }
483
484 static void
485 ProxyEncodeIpHeader(struct ip *pip,
486     int maxpacketsize)
487 {
488 #define OPTION_LEN_BYTES  8
489 #define OPTION_LEN_INT16  4
490 #define OPTION_LEN_INT32  2
491         u_char option[OPTION_LEN_BYTES];
492
493 #ifdef LIBALIAS_DEBUG
494         fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
495         fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
496 #endif
497
498         (void)maxpacketsize;
499
500 /* Check to see that there is room to add an IP option */
501         if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
502                 return;
503
504 /* Build option and copy into packet */
505         {
506                 u_char *ptr;
507                 struct tcphdr *tc;
508
509                 ptr = (u_char *) pip;
510                 ptr += 20;
511                 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
512
513                 option[0] = 0x64;       /* class: 3 (reserved), option 4 */
514                 option[1] = OPTION_LEN_BYTES;
515
516                 memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
517
518                 tc = (struct tcphdr *)ip_next(pip);
519                 memcpy(&option[6], (u_char *) & tc->th_sport, 2);
520
521                 memcpy(ptr, option, 8);
522         }
523
524 /* Update checksum, header length and packet length */
525         {
526                 int i;
527                 int accumulate;
528                 u_short *sptr;
529
530                 sptr = (u_short *) option;
531                 accumulate = 0;
532                 for (i = 0; i < OPTION_LEN_INT16; i++)
533                         accumulate -= *(sptr++);
534
535                 sptr = (u_short *) pip;
536                 accumulate += *sptr;
537                 pip->ip_hl += OPTION_LEN_INT32;
538                 accumulate -= *sptr;
539
540                 accumulate += pip->ip_len;
541                 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
542                 accumulate -= pip->ip_len;
543
544                 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
545         }
546 #undef OPTION_LEN_BYTES
547 #undef OPTION_LEN_INT16
548 #undef OPTION_LEN_INT32
549 #ifdef LIBALIAS_DEBUG
550         fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
551         fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
552 #endif
553 }
554
555
556 /* Functions by other packet alias source files
557
558     ProxyCheck()         -- Checks whether an outgoing packet should
559                             be proxied.
560     ProxyModify()        -- Encodes the original destination address/port
561                             for a packet which is to be redirected to
562                             a proxy server.
563 */
564
565 int
566 ProxyCheck(struct libalias *la, struct ip *pip,
567     struct in_addr *proxy_server_addr,
568     u_short * proxy_server_port)
569 {
570         u_short dst_port;
571         struct in_addr src_addr;
572         struct in_addr dst_addr;
573         struct proxy_entry *ptr;
574
575         src_addr = pip->ip_src;
576         dst_addr = pip->ip_dst;
577         dst_port = ((struct tcphdr *)ip_next(pip))
578             ->th_dport;
579
580         ptr = la->proxyList;
581         while (ptr != NULL) {
582                 u_short proxy_port;
583
584                 proxy_port = ptr->proxy_port;
585                 if ((dst_port == proxy_port || proxy_port == 0)
586                     && pip->ip_p == ptr->proto
587                     && src_addr.s_addr != ptr->server_addr.s_addr) {
588                         struct in_addr src_addr_masked;
589                         struct in_addr dst_addr_masked;
590
591                         src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
592                         dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
593
594                         if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
595                             && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
596                                 if ((*proxy_server_port = ptr->server_port) == 0)
597                                         *proxy_server_port = dst_port;
598                                 *proxy_server_addr = ptr->server_addr;
599                                 return (ptr->proxy_type);
600                         }
601                 }
602                 ptr = ptr->next;
603         }
604
605         return (0);
606 }
607
608 void
609 ProxyModify(struct libalias *la, struct alias_link *lnk,
610     struct ip *pip,
611     int maxpacketsize,
612     int proxy_type)
613 {
614
615         (void)la;
616
617         switch (proxy_type) {
618                 case PROXY_TYPE_ENCODE_IPHDR:
619                 ProxyEncodeIpHeader(pip, maxpacketsize);
620                 break;
621
622         case PROXY_TYPE_ENCODE_TCPSTREAM:
623                 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
624                 break;
625         }
626 }
627
628
629 /*
630     Public API functions
631 */
632
633 int
634 LibAliasProxyRule(struct libalias *la, const char *cmd)
635 {
636 /*
637  * This function takes command strings of the form:
638  *
639  *   server <addr>[:<port>]
640  *   [port <port>]
641  *   [rule n]
642  *   [proto tcp|udp]
643  *   [src <addr>[/n]]
644  *   [dst <addr>[/n]]
645  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
646  *
647  *   delete <rule number>
648  *
649  * Subfields can be in arbitrary order.  Port numbers and addresses
650  * must be in either numeric or symbolic form. An optional rule number
651  * is used to control the order in which rules are searched.  If two
652  * rules have the same number, then search order cannot be guaranteed,
653  * and the rules should be disjoint.  If no rule number is specified,
654  * then 0 is used, and group 0 rules are always checked before any
655  * others.
656  */
657         int i, n, len;
658         int cmd_len;
659         int token_count;
660         int state;
661         char *token;
662         char buffer[256];
663         char str_port[sizeof(buffer)];
664         char str_server_port[sizeof(buffer)];
665         char *res = buffer;
666
667         int rule_index;
668         int proto;
669         int proxy_type;
670         int proxy_port;
671         int server_port;
672         struct in_addr server_addr;
673         struct in_addr src_addr, src_mask;
674         struct in_addr dst_addr, dst_mask;
675         struct proxy_entry *proxy_entry;
676
677 /* Copy command line into a buffer */
678         cmd += strspn(cmd, " \t");
679         cmd_len = strlen(cmd);
680         if (cmd_len > (int)(sizeof(buffer) - 1))
681                 return (-1);
682         strcpy(buffer, cmd);
683
684 /* Convert to lower case */
685         len = strlen(buffer);
686         for (i = 0; i < len; i++)
687                 buffer[i] = tolower((unsigned char)buffer[i]);
688
689 /* Set default proxy type */
690
691 /* Set up default values */
692         rule_index = 0;
693         proxy_type = PROXY_TYPE_ENCODE_NONE;
694         proto = IPPROTO_TCP;
695         proxy_port = 0;
696         server_addr.s_addr = 0;
697         server_port = 0;
698         src_addr.s_addr = 0;
699         IpMask(0, &src_mask);
700         dst_addr.s_addr = 0;
701         IpMask(0, &dst_mask);
702
703         str_port[0] = 0;
704         str_server_port[0] = 0;
705
706 /* Parse command string with state machine */
707 #define STATE_READ_KEYWORD    0
708 #define STATE_READ_TYPE       1
709 #define STATE_READ_PORT       2
710 #define STATE_READ_SERVER     3
711 #define STATE_READ_RULE       4
712 #define STATE_READ_DELETE     5
713 #define STATE_READ_PROTO      6
714 #define STATE_READ_SRC        7
715 #define STATE_READ_DST        8
716         state = STATE_READ_KEYWORD;
717         token = strsep(&res, " \t");
718         token_count = 0;
719         while (token != NULL) {
720                 token_count++;
721                 switch (state) {
722                 case STATE_READ_KEYWORD:
723                         if (strcmp(token, "type") == 0)
724                                 state = STATE_READ_TYPE;
725                         else if (strcmp(token, "port") == 0)
726                                 state = STATE_READ_PORT;
727                         else if (strcmp(token, "server") == 0)
728                                 state = STATE_READ_SERVER;
729                         else if (strcmp(token, "rule") == 0)
730                                 state = STATE_READ_RULE;
731                         else if (strcmp(token, "delete") == 0)
732                                 state = STATE_READ_DELETE;
733                         else if (strcmp(token, "proto") == 0)
734                                 state = STATE_READ_PROTO;
735                         else if (strcmp(token, "src") == 0)
736                                 state = STATE_READ_SRC;
737                         else if (strcmp(token, "dst") == 0)
738                                 state = STATE_READ_DST;
739                         else
740                                 return (-1);
741                         break;
742
743                 case STATE_READ_TYPE:
744                         if (strcmp(token, "encode_ip_hdr") == 0)
745                                 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
746                         else if (strcmp(token, "encode_tcp_stream") == 0)
747                                 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
748                         else if (strcmp(token, "no_encode") == 0)
749                                 proxy_type = PROXY_TYPE_ENCODE_NONE;
750                         else
751                                 return (-1);
752                         state = STATE_READ_KEYWORD;
753                         break;
754
755                 case STATE_READ_PORT:
756                         strcpy(str_port, token);
757                         state = STATE_READ_KEYWORD;
758                         break;
759
760                 case STATE_READ_SERVER:
761                         {
762                                 int err;
763                                 char *p;
764                                 char s[sizeof(buffer)];
765
766                                 p = token;
767                                 while (*p != ':' && *p != 0)
768                                         p++;
769
770                                 if (*p != ':') {
771                                         err = IpAddr(token, &server_addr);
772                                         if (err)
773                                                 return (-1);
774                                 } else {
775                                         *p = ' ';
776
777                                         n = sscanf(token, "%s %s", s, str_server_port);
778                                         if (n != 2)
779                                                 return (-1);
780
781                                         err = IpAddr(s, &server_addr);
782                                         if (err)
783                                                 return (-1);
784                                 }
785                         }
786                         state = STATE_READ_KEYWORD;
787                         break;
788
789                 case STATE_READ_RULE:
790                         n = sscanf(token, "%d", &rule_index);
791                         if (n != 1 || rule_index < 0)
792                                 return (-1);
793                         state = STATE_READ_KEYWORD;
794                         break;
795
796                 case STATE_READ_DELETE:
797                         {
798                                 int err;
799                                 int rule_to_delete;
800
801                                 if (token_count != 2)
802                                         return (-1);
803
804                                 n = sscanf(token, "%d", &rule_to_delete);
805                                 if (n != 1)
806                                         return (-1);
807                                 err = RuleNumberDelete(la, rule_to_delete);
808                                 if (err)
809                                         return (-1);
810                                 return (0);
811                         }
812
813                 case STATE_READ_PROTO:
814                         if (strcmp(token, "tcp") == 0)
815                                 proto = IPPROTO_TCP;
816                         else if (strcmp(token, "udp") == 0)
817                                 proto = IPPROTO_UDP;
818                         else
819                                 return (-1);
820                         state = STATE_READ_KEYWORD;
821                         break;
822
823                 case STATE_READ_SRC:
824                 case STATE_READ_DST:
825                         {
826                                 int err;
827                                 char *p;
828                                 struct in_addr mask;
829                                 struct in_addr addr;
830
831                                 p = token;
832                                 while (*p != '/' && *p != 0)
833                                         p++;
834
835                                 if (*p != '/') {
836                                         IpMask(32, &mask);
837                                         err = IpAddr(token, &addr);
838                                         if (err)
839                                                 return (-1);
840                                 } else {
841                                         int nbits;
842                                         char s[sizeof(buffer)];
843
844                                         *p = ' ';
845                                         n = sscanf(token, "%s %d", s, &nbits);
846                                         if (n != 2)
847                                                 return (-1);
848
849                                         err = IpAddr(s, &addr);
850                                         if (err)
851                                                 return (-1);
852
853                                         err = IpMask(nbits, &mask);
854                                         if (err)
855                                                 return (-1);
856                                 }
857
858                                 if (state == STATE_READ_SRC) {
859                                         src_addr = addr;
860                                         src_mask = mask;
861                                 } else {
862                                         dst_addr = addr;
863                                         dst_mask = mask;
864                                 }
865                         }
866                         state = STATE_READ_KEYWORD;
867                         break;
868
869                 default:
870                         return (-1);
871                         break;
872                 }
873
874                 do {
875                         token = strsep(&res, " \t");
876                 } while (token != NULL && !*token);
877         }
878 #undef STATE_READ_KEYWORD
879 #undef STATE_READ_TYPE
880 #undef STATE_READ_PORT
881 #undef STATE_READ_SERVER
882 #undef STATE_READ_RULE
883 #undef STATE_READ_DELETE
884 #undef STATE_READ_PROTO
885 #undef STATE_READ_SRC
886 #undef STATE_READ_DST
887
888 /* Convert port strings to numbers.  This needs to be done after
889    the string is parsed, because the prototype might not be designated
890    before the ports (which might be symbolic entries in /etc/services) */
891
892         if (strlen(str_port) != 0) {
893                 int err;
894
895                 err = IpPort(str_port, proto, &proxy_port);
896                 if (err)
897                         return (-1);
898         } else {
899                 proxy_port = 0;
900         }
901
902         if (strlen(str_server_port) != 0) {
903                 int err;
904
905                 err = IpPort(str_server_port, proto, &server_port);
906                 if (err)
907                         return (-1);
908         } else {
909                 server_port = 0;
910         }
911
912 /* Check that at least the server address has been defined */
913         if (server_addr.s_addr == 0)
914                 return (-1);
915
916 /* Add to linked list */
917         proxy_entry = malloc(sizeof(struct proxy_entry));
918         if (proxy_entry == NULL)
919                 return (-1);
920
921         proxy_entry->proxy_type = proxy_type;
922         proxy_entry->rule_index = rule_index;
923         proxy_entry->proto = proto;
924         proxy_entry->proxy_port = htons(proxy_port);
925         proxy_entry->server_port = htons(server_port);
926         proxy_entry->server_addr = server_addr;
927         proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
928         proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
929         proxy_entry->src_mask = src_mask;
930         proxy_entry->dst_mask = dst_mask;
931
932         RuleAdd(la, proxy_entry);
933
934         return (0);
935 }