]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - sys/netinet/libalias/alias_proxy.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.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/limits.h>
62 #else
63 #include <sys/types.h>
64 #include <ctype.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <netdb.h>
68 #include <string.h>
69 #endif
70
71 #include <netinet/tcp.h>
72
73 #ifdef _KERNEL
74 #include <netinet/libalias/alias.h>
75 #include <netinet/libalias/alias_local.h>
76 #include <netinet/libalias/alias_mod.h>
77 #else
78 #include <arpa/inet.h>
79 #include "alias.h"              /* Public API functions for libalias */
80 #include "alias_local.h"        /* Functions used by alias*.c */
81 #endif
82
83 /*
84     Data structures
85  */
86
87 /*
88  * A linked list of arbitrary length, based on struct proxy_entry is
89  * used to store proxy rules.
90  */
91 struct proxy_entry {
92         struct libalias *la;
93 #define PROXY_TYPE_ENCODE_NONE      1
94 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
95 #define PROXY_TYPE_ENCODE_IPHDR     3
96         int             rule_index;
97         int             proxy_type;
98         u_char          proto;
99         u_short         proxy_port;
100         u_short         server_port;
101
102         struct in_addr  server_addr;
103
104         struct in_addr  src_addr;
105         struct in_addr  src_mask;
106
107         struct in_addr  dst_addr;
108         struct in_addr  dst_mask;
109
110         struct proxy_entry *next;
111         struct proxy_entry *last;
112 };
113
114
115
116 /*
117     File scope variables
118 */
119
120
121
122 /* Local (static) functions:
123
124     IpMask()                 -- Utility function for creating IP
125                                 masks from integer (1-32) specification.
126     IpAddr()                 -- Utility function for converting string
127                                 to IP address
128     IpPort()                 -- Utility function for converting string
129                                 to port number
130     RuleAdd()                -- Adds an element to the rule list.
131     RuleDelete()             -- Removes an element from the rule list.
132     RuleNumberDelete()       -- Removes all elements from the rule list
133                                 having a certain rule number.
134     ProxyEncodeTcpStream()   -- Adds [DEST x.x.x.x xxxx] to the beginning
135                                 of a TCP stream.
136     ProxyEncodeIpHeader()    -- Adds an IP option indicating the true
137                                 destination of a proxied IP packet
138 */
139
140 #ifdef  _KERNEL         /* XXX: can it be moved to libkern? */
141 static int inet_aton(const char *cp, struct in_addr *addr);
142 #endif
143 static int      IpMask(int, struct in_addr *);
144 static int      IpAddr(char *, struct in_addr *);
145 static int      IpPort(char *, int, int *);
146 static void     RuleAdd(struct libalias *la, struct proxy_entry *);
147 static void     RuleDelete(struct proxy_entry *);
148 static int      RuleNumberDelete(struct libalias *la, int);
149 static void     ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
150 static void     ProxyEncodeIpHeader(struct ip *, int);
151
152 #ifdef  _KERNEL
153 static int
154 inet_aton(cp, addr)
155         const char *cp;
156         struct in_addr *addr;
157 {
158         u_long parts[4];
159         in_addr_t val;
160         const char *c;
161         char *endptr;
162         int gotend, n;
163
164         c = (const char *)cp;
165         n = 0;
166         /*
167          * Run through the string, grabbing numbers until
168          * the end of the string, or some error
169          */
170         gotend = 0;
171         while (!gotend) {
172                 unsigned long l;
173
174                 l = strtoul(c, &endptr, 0);
175
176                 if (l == ULONG_MAX || (l == 0 && endptr == c))
177                         return (0);
178
179                 val = (in_addr_t)l;
180                 /*
181                  * If the whole string is invalid, endptr will equal
182                  * c.. this way we can make sure someone hasn't
183                  * gone '.12' or something which would get past
184                  * the next check.
185                  */
186                 if (endptr == c)
187                         return (0);
188                 parts[n] = val;
189                 c = endptr;
190
191                 /* Check the next character past the previous number's end */
192                 switch (*c) {
193                 case '.' :
194                         /* Make sure we only do 3 dots .. */
195                         if (n == 3)     /* Whoops. Quit. */
196                                 return (0);
197                         n++;
198                         c++;
199                         break;
200
201                 case '\0':
202                         gotend = 1;
203                         break;
204
205                 default:
206                         if (isspace((unsigned char)*c)) {
207                                 gotend = 1;
208                                 break;
209                         } else
210                                 return (0);     /* Invalid character, so fail */
211                 }
212
213         }
214
215         /*
216          * Concoct the address according to
217          * the number of parts specified.
218          */
219
220         switch (n) {
221         case 0:                         /* a -- 32 bits */
222                 /*
223                  * Nothing is necessary here.  Overflow checking was
224                  * already done in strtoul().
225                  */
226                 break;
227         case 1:                         /* a.b -- 8.24 bits */
228                 if (val > 0xffffff || parts[0] > 0xff)
229                         return (0);
230                 val |= parts[0] << 24;
231                 break;
232
233         case 2:                         /* a.b.c -- 8.8.16 bits */
234                 if (val > 0xffff || parts[0] > 0xff || parts[1] > 0xff)
235                         return (0);
236                 val |= (parts[0] << 24) | (parts[1] << 16);
237                 break;
238
239         case 3:                         /* a.b.c.d -- 8.8.8.8 bits */
240                 if (val > 0xff || parts[0] > 0xff || parts[1] > 0xff ||
241                     parts[2] > 0xff)
242                         return (0);
243                 val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
244                 break;
245         }
246
247         if (addr != NULL)
248                 addr->s_addr = htonl(val);
249         return (1);
250 }
251 #endif
252
253 static int
254 IpMask(int nbits, struct in_addr *mask)
255 {
256         int i;
257         u_int imask;
258
259         if (nbits < 0 || nbits > 32)
260                 return (-1);
261
262         imask = 0;
263         for (i = 0; i < nbits; i++)
264                 imask = (imask >> 1) + 0x80000000;
265         mask->s_addr = htonl(imask);
266
267         return (0);
268 }
269
270 static int
271 IpAddr(char *s, struct in_addr *addr)
272 {
273         if (inet_aton(s, addr) == 0)
274                 return (-1);
275         else
276                 return (0);
277 }
278
279 static int
280 IpPort(char *s, int proto, int *port)
281 {
282         int n;
283
284         n = sscanf(s, "%d", port);
285         if (n != 1)
286 #ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
287         {
288                 struct servent *se;
289
290                 if (proto == IPPROTO_TCP)
291                         se = getservbyname(s, "tcp");
292                 else if (proto == IPPROTO_UDP)
293                         se = getservbyname(s, "udp");
294                 else
295                         return (-1);
296
297                 if (se == NULL)
298                         return (-1);
299
300                 *port = (u_int) ntohs(se->s_port);
301         }
302 #else
303                 return (-1);
304 #endif
305         return (0);
306 }
307
308 void
309 RuleAdd(struct libalias *la, struct proxy_entry *entry)
310 {
311         int rule_index;
312         struct proxy_entry *ptr;
313         struct proxy_entry *ptr_last;
314
315         LIBALIAS_LOCK_ASSERT(la);
316
317         if (la->proxyList == NULL) {
318                 la->proxyList = entry;
319                 entry->last = NULL;
320                 entry->next = NULL;
321                 return;
322         }
323         entry->la = la;
324
325         rule_index = entry->rule_index;
326         ptr = la->proxyList;
327         ptr_last = NULL;
328         while (ptr != NULL) {
329                 if (ptr->rule_index >= rule_index) {
330                         if (ptr_last == NULL) {
331                                 entry->next = la->proxyList;
332                                 entry->last = NULL;
333                                 la->proxyList->last = entry;
334                                 la->proxyList = entry;
335                                 return;
336                         }
337                         ptr_last->next = entry;
338                         ptr->last = entry;
339                         entry->last = ptr->last;
340                         entry->next = ptr;
341                         return;
342                 }
343                 ptr_last = ptr;
344                 ptr = ptr->next;
345         }
346
347         ptr_last->next = entry;
348         entry->last = ptr_last;
349         entry->next = NULL;
350 }
351
352 static void
353 RuleDelete(struct proxy_entry *entry)
354 {
355         struct libalias *la;
356
357         la = entry->la;
358         LIBALIAS_LOCK_ASSERT(la);
359         if (entry->last != NULL)
360                 entry->last->next = entry->next;
361         else
362                 la->proxyList = entry->next;
363
364         if (entry->next != NULL)
365                 entry->next->last = entry->last;
366
367         free(entry);
368 }
369
370 static int
371 RuleNumberDelete(struct libalias *la, int rule_index)
372 {
373         int err;
374         struct proxy_entry *ptr;
375
376         LIBALIAS_LOCK_ASSERT(la);
377         err = -1;
378         ptr = la->proxyList;
379         while (ptr != NULL) {
380                 struct proxy_entry *ptr_next;
381
382                 ptr_next = ptr->next;
383                 if (ptr->rule_index == rule_index) {
384                         err = 0;
385                         RuleDelete(ptr);
386                 }
387                 ptr = ptr_next;
388         }
389
390         return (err);
391 }
392
393 static void
394 ProxyEncodeTcpStream(struct alias_link *lnk,
395     struct ip *pip,
396     int maxpacketsize)
397 {
398         int slen;
399         char buffer[40];
400         struct tcphdr *tc;
401
402 /* Compute pointer to tcp header */
403         tc = (struct tcphdr *)ip_next(pip);
404
405 /* Don't modify if once already modified */
406
407         if (GetAckModified(lnk))
408                 return;
409
410 /* Translate destination address and port to string form */
411         snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
412             inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
413
414 /* Pad string out to a multiple of two in length */
415         slen = strlen(buffer);
416         switch (slen % 2) {
417         case 0:
418                 strcat(buffer, " \n");
419                 slen += 2;
420                 break;
421         case 1:
422                 strcat(buffer, "\n");
423                 slen += 1;
424         }
425
426 /* Check for packet overflow */
427         if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
428                 return;
429
430 /* Shift existing TCP data and insert destination string */
431         {
432                 int dlen;
433                 int hlen;
434                 char *p;
435
436                 hlen = (pip->ip_hl + tc->th_off) << 2;
437                 dlen = ntohs(pip->ip_len) - hlen;
438
439 /* Modify first packet that has data in it */
440
441                 if (dlen == 0)
442                         return;
443
444                 p = (char *)pip;
445                 p += hlen;
446
447                 bcopy(p, p + slen, dlen);
448                 memcpy(p, buffer, slen);
449         }
450
451 /* Save information about modfied sequence number */
452         {
453                 int delta;
454
455                 SetAckModified(lnk);
456                 tc = (struct tcphdr *)ip_next(pip);                     
457                 delta = GetDeltaSeqOut(tc->th_seq, lnk);
458                 AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
459                     tc->th_off);
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 in_addr *proxy_server_addr,
567     u_short * proxy_server_port, struct in_addr src_addr, 
568     struct in_addr dst_addr, u_short dst_port, u_char ip_p)
569 {
570         struct proxy_entry *ptr;
571
572         LIBALIAS_LOCK_ASSERT(la);
573
574         ptr = la->proxyList;
575         while (ptr != NULL) {
576                 u_short proxy_port;
577
578                 proxy_port = ptr->proxy_port;
579                 if ((dst_port == proxy_port || proxy_port == 0)
580                     && ip_p == ptr->proto
581                     && src_addr.s_addr != ptr->server_addr.s_addr) {
582                         struct in_addr src_addr_masked;
583                         struct in_addr dst_addr_masked;
584
585                         src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
586                         dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
587
588                         if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
589                             && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
590                                 if ((*proxy_server_port = ptr->server_port) == 0)
591                                         *proxy_server_port = dst_port;
592                                 *proxy_server_addr = ptr->server_addr;
593                                 return (ptr->proxy_type);
594                         }
595                 }
596                 ptr = ptr->next;
597         }
598
599         return (0);
600 }
601
602 void
603 ProxyModify(struct libalias *la, struct alias_link *lnk,
604     struct ip *pip,
605     int maxpacketsize,
606     int proxy_type)
607 {
608
609         LIBALIAS_LOCK_ASSERT(la);
610         (void)la;
611
612         switch (proxy_type) {
613                 case PROXY_TYPE_ENCODE_IPHDR:
614                 ProxyEncodeIpHeader(pip, maxpacketsize);
615                 break;
616
617         case PROXY_TYPE_ENCODE_TCPSTREAM:
618                 ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
619                 break;
620         }
621 }
622
623
624 /*
625     Public API functions
626 */
627
628 int
629 LibAliasProxyRule(struct libalias *la, const char *cmd)
630 {
631 /*
632  * This function takes command strings of the form:
633  *
634  *   server <addr>[:<port>]
635  *   [port <port>]
636  *   [rule n]
637  *   [proto tcp|udp]
638  *   [src <addr>[/n]]
639  *   [dst <addr>[/n]]
640  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
641  *
642  *   delete <rule number>
643  *
644  * Subfields can be in arbitrary order.  Port numbers and addresses
645  * must be in either numeric or symbolic form. An optional rule number
646  * is used to control the order in which rules are searched.  If two
647  * rules have the same number, then search order cannot be guaranteed,
648  * and the rules should be disjoint.  If no rule number is specified,
649  * then 0 is used, and group 0 rules are always checked before any
650  * others.
651  */
652         int i, n, len, ret;
653         int cmd_len;
654         int token_count;
655         int state;
656         char *token;
657         char buffer[256];
658         char str_port[sizeof(buffer)];
659         char str_server_port[sizeof(buffer)];
660         char *res = buffer;
661
662         int rule_index;
663         int proto;
664         int proxy_type;
665         int proxy_port;
666         int server_port;
667         struct in_addr server_addr;
668         struct in_addr src_addr, src_mask;
669         struct in_addr dst_addr, dst_mask;
670         struct proxy_entry *proxy_entry;
671
672         LIBALIAS_LOCK(la);
673         ret = 0;
674 /* Copy command line into a buffer */
675         cmd += strspn(cmd, " \t");
676         cmd_len = strlen(cmd);
677         if (cmd_len > (int)(sizeof(buffer) - 1)) {
678                 ret = -1;
679                 goto getout;
680         }
681         strcpy(buffer, cmd);
682
683 /* Convert to lower case */
684         len = strlen(buffer);
685         for (i = 0; i < len; i++)
686                 buffer[i] = tolower((unsigned char)buffer[i]);
687
688 /* Set default proxy type */
689
690 /* Set up default values */
691         rule_index = 0;
692         proxy_type = PROXY_TYPE_ENCODE_NONE;
693         proto = IPPROTO_TCP;
694         proxy_port = 0;
695         server_addr.s_addr = 0;
696         server_port = 0;
697         src_addr.s_addr = 0;
698         IpMask(0, &src_mask);
699         dst_addr.s_addr = 0;
700         IpMask(0, &dst_mask);
701
702         str_port[0] = 0;
703         str_server_port[0] = 0;
704
705 /* Parse command string with state machine */
706 #define STATE_READ_KEYWORD    0
707 #define STATE_READ_TYPE       1
708 #define STATE_READ_PORT       2
709 #define STATE_READ_SERVER     3
710 #define STATE_READ_RULE       4
711 #define STATE_READ_DELETE     5
712 #define STATE_READ_PROTO      6
713 #define STATE_READ_SRC        7
714 #define STATE_READ_DST        8
715         state = STATE_READ_KEYWORD;
716         token = strsep(&res, " \t");
717         token_count = 0;
718         while (token != NULL) {
719                 token_count++;
720                 switch (state) {
721                 case STATE_READ_KEYWORD:
722                         if (strcmp(token, "type") == 0)
723                                 state = STATE_READ_TYPE;
724                         else if (strcmp(token, "port") == 0)
725                                 state = STATE_READ_PORT;
726                         else if (strcmp(token, "server") == 0)
727                                 state = STATE_READ_SERVER;
728                         else if (strcmp(token, "rule") == 0)
729                                 state = STATE_READ_RULE;
730                         else if (strcmp(token, "delete") == 0)
731                                 state = STATE_READ_DELETE;
732                         else if (strcmp(token, "proto") == 0)
733                                 state = STATE_READ_PROTO;
734                         else if (strcmp(token, "src") == 0)
735                                 state = STATE_READ_SRC;
736                         else if (strcmp(token, "dst") == 0)
737                                 state = STATE_READ_DST;
738                         else {
739                                 ret = -1;
740                                 goto getout;
741                         }
742                         break;
743
744                 case STATE_READ_TYPE:
745                         if (strcmp(token, "encode_ip_hdr") == 0)
746                                 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
747                         else if (strcmp(token, "encode_tcp_stream") == 0)
748                                 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
749                         else if (strcmp(token, "no_encode") == 0)
750                                 proxy_type = PROXY_TYPE_ENCODE_NONE;
751                         else {
752                                 ret = -1;
753                                 goto getout;
754                         }
755                         state = STATE_READ_KEYWORD;
756                         break;
757
758                 case STATE_READ_PORT:
759                         strcpy(str_port, token);
760                         state = STATE_READ_KEYWORD;
761                         break;
762
763                 case STATE_READ_SERVER:
764                         {
765                                 int err;
766                                 char *p;
767                                 char s[sizeof(buffer)];
768
769                                 p = token;
770                                 while (*p != ':' && *p != 0)
771                                         p++;
772
773                                 if (*p != ':') {
774                                         err = IpAddr(token, &server_addr);
775                                         if (err) {
776                                                 ret = -1;
777                                                 goto getout;
778                                         }
779                                 } else {
780                                         *p = ' ';
781
782                                         n = sscanf(token, "%s %s", s, str_server_port);
783                                         if (n != 2) {
784                                                 ret = -1;
785                                                 goto getout;
786                                         }
787
788                                         err = IpAddr(s, &server_addr);
789                                         if (err) {
790                                                 ret = -1;
791                                                 goto getout;
792                                         }
793                                 }
794                         }
795                         state = STATE_READ_KEYWORD;
796                         break;
797
798                 case STATE_READ_RULE:
799                         n = sscanf(token, "%d", &rule_index);
800                         if (n != 1 || rule_index < 0) {
801                                 ret = -1;
802                                 goto getout;
803                         }
804                         state = STATE_READ_KEYWORD;
805                         break;
806
807                 case STATE_READ_DELETE:
808                         {
809                                 int err;
810                                 int rule_to_delete;
811
812                                 if (token_count != 2) {
813                                         ret = -1;
814                                         goto getout;
815                                 }
816
817                                 n = sscanf(token, "%d", &rule_to_delete);
818                                 if (n != 1) {
819                                         ret = -1;
820                                         goto getout;
821                                 }
822                                 err = RuleNumberDelete(la, rule_to_delete);
823                                 if (err)
824                                         ret = -1;
825                                 ret = 0;
826                                 goto getout;
827                         }
828
829                 case STATE_READ_PROTO:
830                         if (strcmp(token, "tcp") == 0)
831                                 proto = IPPROTO_TCP;
832                         else if (strcmp(token, "udp") == 0)
833                                 proto = IPPROTO_UDP;
834                         else {
835                                 ret = -1;
836                                 goto getout;
837                         }
838                         state = STATE_READ_KEYWORD;
839                         break;
840
841                 case STATE_READ_SRC:
842                 case STATE_READ_DST:
843                         {
844                                 int err;
845                                 char *p;
846                                 struct in_addr mask;
847                                 struct in_addr addr;
848
849                                 p = token;
850                                 while (*p != '/' && *p != 0)
851                                         p++;
852
853                                 if (*p != '/') {
854                                         IpMask(32, &mask);
855                                         err = IpAddr(token, &addr);
856                                         if (err) {
857                                                 ret = -1;
858                                                 goto getout;
859                                         }
860                                 } else {
861                                         int nbits;
862                                         char s[sizeof(buffer)];
863
864                                         *p = ' ';
865                                         n = sscanf(token, "%s %d", s, &nbits);
866                                         if (n != 2) {
867                                                 ret = -1;
868                                                 goto getout;
869                                         }
870
871                                         err = IpAddr(s, &addr);
872                                         if (err) {
873                                                 ret = -1;
874                                                 goto getout;
875                                         }
876
877                                         err = IpMask(nbits, &mask);
878                                         if (err) {
879                                                 ret = -1;
880                                                 goto getout;
881                                         }
882                                 }
883
884                                 if (state == STATE_READ_SRC) {
885                                         src_addr = addr;
886                                         src_mask = mask;
887                                 } else {
888                                         dst_addr = addr;
889                                         dst_mask = mask;
890                                 }
891                         }
892                         state = STATE_READ_KEYWORD;
893                         break;
894
895                 default:
896                         ret = -1;
897                         goto getout;
898                         break;
899                 }
900
901                 do {
902                         token = strsep(&res, " \t");
903                 } while (token != NULL && !*token);
904         }
905 #undef STATE_READ_KEYWORD
906 #undef STATE_READ_TYPE
907 #undef STATE_READ_PORT
908 #undef STATE_READ_SERVER
909 #undef STATE_READ_RULE
910 #undef STATE_READ_DELETE
911 #undef STATE_READ_PROTO
912 #undef STATE_READ_SRC
913 #undef STATE_READ_DST
914
915 /* Convert port strings to numbers.  This needs to be done after
916    the string is parsed, because the prototype might not be designated
917    before the ports (which might be symbolic entries in /etc/services) */
918
919         if (strlen(str_port) != 0) {
920                 int err;
921
922                 err = IpPort(str_port, proto, &proxy_port);
923                 if (err) {
924                         ret = -1;
925                         goto getout;
926                 }
927         } else {
928                 proxy_port = 0;
929         }
930
931         if (strlen(str_server_port) != 0) {
932                 int err;
933
934                 err = IpPort(str_server_port, proto, &server_port);
935                 if (err) {
936                         ret = -1;
937                         goto getout;
938                 }
939         } else {
940                 server_port = 0;
941         }
942
943 /* Check that at least the server address has been defined */
944         if (server_addr.s_addr == 0) {
945                 ret = -1;
946                 goto getout;
947         }
948
949 /* Add to linked list */
950         proxy_entry = malloc(sizeof(struct proxy_entry));
951         if (proxy_entry == NULL) {
952                 ret = -1;
953                 goto getout;
954         }
955
956         proxy_entry->proxy_type = proxy_type;
957         proxy_entry->rule_index = rule_index;
958         proxy_entry->proto = proto;
959         proxy_entry->proxy_port = htons(proxy_port);
960         proxy_entry->server_port = htons(server_port);
961         proxy_entry->server_addr = server_addr;
962         proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
963         proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
964         proxy_entry->src_mask = src_mask;
965         proxy_entry->dst_mask = dst_mask;
966
967         RuleAdd(la, proxy_entry);
968
969 getout:
970         LIBALIAS_UNLOCK(la);
971         return (ret);
972 }