]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/netinet/libalias/alias_proxy.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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                 delta = GetDeltaSeqOut(pip, lnk);
457                 AddSeq(pip, lnk, delta + slen);
458         }
459
460 /* Update IP header packet length and checksum */
461         {
462                 int accumulate;
463
464                 accumulate = pip->ip_len;
465                 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
466                 accumulate -= pip->ip_len;
467
468                 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
469         }
470
471 /* Update TCP checksum, Use TcpChecksum since so many things have
472    already changed. */
473
474         tc->th_sum = 0;
475 #ifdef _KERNEL
476         tc->th_x2 = 1;
477 #else
478         tc->th_sum = TcpChecksum(pip);
479 #endif
480 }
481
482 static void
483 ProxyEncodeIpHeader(struct ip *pip,
484     int maxpacketsize)
485 {
486 #define OPTION_LEN_BYTES  8
487 #define OPTION_LEN_INT16  4
488 #define OPTION_LEN_INT32  2
489         u_char option[OPTION_LEN_BYTES];
490
491 #ifdef LIBALIAS_DEBUG
492         fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
493         fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
494 #endif
495
496         (void)maxpacketsize;
497
498 /* Check to see that there is room to add an IP option */
499         if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
500                 return;
501
502 /* Build option and copy into packet */
503         {
504                 u_char *ptr;
505                 struct tcphdr *tc;
506
507                 ptr = (u_char *) pip;
508                 ptr += 20;
509                 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
510
511                 option[0] = 0x64;       /* class: 3 (reserved), option 4 */
512                 option[1] = OPTION_LEN_BYTES;
513
514                 memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
515
516                 tc = (struct tcphdr *)ip_next(pip);
517                 memcpy(&option[6], (u_char *) & tc->th_sport, 2);
518
519                 memcpy(ptr, option, 8);
520         }
521
522 /* Update checksum, header length and packet length */
523         {
524                 int i;
525                 int accumulate;
526                 u_short *sptr;
527
528                 sptr = (u_short *) option;
529                 accumulate = 0;
530                 for (i = 0; i < OPTION_LEN_INT16; i++)
531                         accumulate -= *(sptr++);
532
533                 sptr = (u_short *) pip;
534                 accumulate += *sptr;
535                 pip->ip_hl += OPTION_LEN_INT32;
536                 accumulate -= *sptr;
537
538                 accumulate += pip->ip_len;
539                 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
540                 accumulate -= pip->ip_len;
541
542                 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
543         }
544 #undef OPTION_LEN_BYTES
545 #undef OPTION_LEN_INT16
546 #undef OPTION_LEN_INT32
547 #ifdef LIBALIAS_DEBUG
548         fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
549         fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
550 #endif
551 }
552
553
554 /* Functions by other packet alias source files
555
556     ProxyCheck()         -- Checks whether an outgoing packet should
557                             be proxied.
558     ProxyModify()        -- Encodes the original destination address/port
559                             for a packet which is to be redirected to
560                             a proxy server.
561 */
562
563 int
564 ProxyCheck(struct libalias *la, struct ip *pip,
565     struct in_addr *proxy_server_addr,
566     u_short * proxy_server_port)
567 {
568         u_short dst_port;
569         struct in_addr src_addr;
570         struct in_addr dst_addr;
571         struct proxy_entry *ptr;
572
573         LIBALIAS_LOCK_ASSERT(la);
574         src_addr = pip->ip_src;
575         dst_addr = pip->ip_dst;
576         dst_port = ((struct tcphdr *)ip_next(pip))
577             ->th_dport;
578
579         ptr = la->proxyList;
580         while (ptr != NULL) {
581                 u_short proxy_port;
582
583                 proxy_port = ptr->proxy_port;
584                 if ((dst_port == proxy_port || proxy_port == 0)
585                     && pip->ip_p == ptr->proto
586                     && src_addr.s_addr != ptr->server_addr.s_addr) {
587                         struct in_addr src_addr_masked;
588                         struct in_addr dst_addr_masked;
589
590                         src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
591                         dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
592
593                         if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
594                             && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
595                                 if ((*proxy_server_port = ptr->server_port) == 0)
596                                         *proxy_server_port = dst_port;
597                                 *proxy_server_addr = ptr->server_addr;
598                                 return (ptr->proxy_type);
599                         }
600                 }
601                 ptr = ptr->next;
602         }
603
604         return (0);
605 }
606
607 void
608 ProxyModify(struct libalias *la, struct alias_link *lnk,
609     struct ip *pip,
610     int maxpacketsize,
611     int proxy_type)
612 {
613
614         LIBALIAS_LOCK_ASSERT(la);
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, ret;
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         LIBALIAS_LOCK(la);
678         ret = 0;
679 /* Copy command line into a buffer */
680         cmd += strspn(cmd, " \t");
681         cmd_len = strlen(cmd);
682         if (cmd_len > (int)(sizeof(buffer) - 1)) {
683                 ret = -1;
684                 goto getout;
685         }
686         strcpy(buffer, cmd);
687
688 /* Convert to lower case */
689         len = strlen(buffer);
690         for (i = 0; i < len; i++)
691                 buffer[i] = tolower((unsigned char)buffer[i]);
692
693 /* Set default proxy type */
694
695 /* Set up default values */
696         rule_index = 0;
697         proxy_type = PROXY_TYPE_ENCODE_NONE;
698         proto = IPPROTO_TCP;
699         proxy_port = 0;
700         server_addr.s_addr = 0;
701         server_port = 0;
702         src_addr.s_addr = 0;
703         IpMask(0, &src_mask);
704         dst_addr.s_addr = 0;
705         IpMask(0, &dst_mask);
706
707         str_port[0] = 0;
708         str_server_port[0] = 0;
709
710 /* Parse command string with state machine */
711 #define STATE_READ_KEYWORD    0
712 #define STATE_READ_TYPE       1
713 #define STATE_READ_PORT       2
714 #define STATE_READ_SERVER     3
715 #define STATE_READ_RULE       4
716 #define STATE_READ_DELETE     5
717 #define STATE_READ_PROTO      6
718 #define STATE_READ_SRC        7
719 #define STATE_READ_DST        8
720         state = STATE_READ_KEYWORD;
721         token = strsep(&res, " \t");
722         token_count = 0;
723         while (token != NULL) {
724                 token_count++;
725                 switch (state) {
726                 case STATE_READ_KEYWORD:
727                         if (strcmp(token, "type") == 0)
728                                 state = STATE_READ_TYPE;
729                         else if (strcmp(token, "port") == 0)
730                                 state = STATE_READ_PORT;
731                         else if (strcmp(token, "server") == 0)
732                                 state = STATE_READ_SERVER;
733                         else if (strcmp(token, "rule") == 0)
734                                 state = STATE_READ_RULE;
735                         else if (strcmp(token, "delete") == 0)
736                                 state = STATE_READ_DELETE;
737                         else if (strcmp(token, "proto") == 0)
738                                 state = STATE_READ_PROTO;
739                         else if (strcmp(token, "src") == 0)
740                                 state = STATE_READ_SRC;
741                         else if (strcmp(token, "dst") == 0)
742                                 state = STATE_READ_DST;
743                         else {
744                                 ret = -1;
745                                 goto getout;
746                         }
747                         break;
748
749                 case STATE_READ_TYPE:
750                         if (strcmp(token, "encode_ip_hdr") == 0)
751                                 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
752                         else if (strcmp(token, "encode_tcp_stream") == 0)
753                                 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
754                         else if (strcmp(token, "no_encode") == 0)
755                                 proxy_type = PROXY_TYPE_ENCODE_NONE;
756                         else {
757                                 ret = -1;
758                                 goto getout;
759                         }
760                         state = STATE_READ_KEYWORD;
761                         break;
762
763                 case STATE_READ_PORT:
764                         strcpy(str_port, token);
765                         state = STATE_READ_KEYWORD;
766                         break;
767
768                 case STATE_READ_SERVER:
769                         {
770                                 int err;
771                                 char *p;
772                                 char s[sizeof(buffer)];
773
774                                 p = token;
775                                 while (*p != ':' && *p != 0)
776                                         p++;
777
778                                 if (*p != ':') {
779                                         err = IpAddr(token, &server_addr);
780                                         if (err) {
781                                                 ret = -1;
782                                                 goto getout;
783                                         }
784                                 } else {
785                                         *p = ' ';
786
787                                         n = sscanf(token, "%s %s", s, str_server_port);
788                                         if (n != 2) {
789                                                 ret = -1;
790                                                 goto getout;
791                                         }
792
793                                         err = IpAddr(s, &server_addr);
794                                         if (err) {
795                                                 ret = -1;
796                                                 goto getout;
797                                         }
798                                 }
799                         }
800                         state = STATE_READ_KEYWORD;
801                         break;
802
803                 case STATE_READ_RULE:
804                         n = sscanf(token, "%d", &rule_index);
805                         if (n != 1 || rule_index < 0) {
806                                 ret = -1;
807                                 goto getout;
808                         }
809                         state = STATE_READ_KEYWORD;
810                         break;
811
812                 case STATE_READ_DELETE:
813                         {
814                                 int err;
815                                 int rule_to_delete;
816
817                                 if (token_count != 2) {
818                                         ret = -1;
819                                         goto getout;
820                                 }
821
822                                 n = sscanf(token, "%d", &rule_to_delete);
823                                 if (n != 1) {
824                                         ret = -1;
825                                         goto getout;
826                                 }
827                                 err = RuleNumberDelete(la, rule_to_delete);
828                                 if (err)
829                                         ret = -1;
830                                 ret = 0;
831                                 goto getout;
832                         }
833
834                 case STATE_READ_PROTO:
835                         if (strcmp(token, "tcp") == 0)
836                                 proto = IPPROTO_TCP;
837                         else if (strcmp(token, "udp") == 0)
838                                 proto = IPPROTO_UDP;
839                         else {
840                                 ret = -1;
841                                 goto getout;
842                         }
843                         state = STATE_READ_KEYWORD;
844                         break;
845
846                 case STATE_READ_SRC:
847                 case STATE_READ_DST:
848                         {
849                                 int err;
850                                 char *p;
851                                 struct in_addr mask;
852                                 struct in_addr addr;
853
854                                 p = token;
855                                 while (*p != '/' && *p != 0)
856                                         p++;
857
858                                 if (*p != '/') {
859                                         IpMask(32, &mask);
860                                         err = IpAddr(token, &addr);
861                                         if (err) {
862                                                 ret = -1;
863                                                 goto getout;
864                                         }
865                                 } else {
866                                         int nbits;
867                                         char s[sizeof(buffer)];
868
869                                         *p = ' ';
870                                         n = sscanf(token, "%s %d", s, &nbits);
871                                         if (n != 2) {
872                                                 ret = -1;
873                                                 goto getout;
874                                         }
875
876                                         err = IpAddr(s, &addr);
877                                         if (err) {
878                                                 ret = -1;
879                                                 goto getout;
880                                         }
881
882                                         err = IpMask(nbits, &mask);
883                                         if (err) {
884                                                 ret = -1;
885                                                 goto getout;
886                                         }
887                                 }
888
889                                 if (state == STATE_READ_SRC) {
890                                         src_addr = addr;
891                                         src_mask = mask;
892                                 } else {
893                                         dst_addr = addr;
894                                         dst_mask = mask;
895                                 }
896                         }
897                         state = STATE_READ_KEYWORD;
898                         break;
899
900                 default:
901                         ret = -1;
902                         goto getout;
903                         break;
904                 }
905
906                 do {
907                         token = strsep(&res, " \t");
908                 } while (token != NULL && !*token);
909         }
910 #undef STATE_READ_KEYWORD
911 #undef STATE_READ_TYPE
912 #undef STATE_READ_PORT
913 #undef STATE_READ_SERVER
914 #undef STATE_READ_RULE
915 #undef STATE_READ_DELETE
916 #undef STATE_READ_PROTO
917 #undef STATE_READ_SRC
918 #undef STATE_READ_DST
919
920 /* Convert port strings to numbers.  This needs to be done after
921    the string is parsed, because the prototype might not be designated
922    before the ports (which might be symbolic entries in /etc/services) */
923
924         if (strlen(str_port) != 0) {
925                 int err;
926
927                 err = IpPort(str_port, proto, &proxy_port);
928                 if (err) {
929                         ret = -1;
930                         goto getout;
931                 }
932         } else {
933                 proxy_port = 0;
934         }
935
936         if (strlen(str_server_port) != 0) {
937                 int err;
938
939                 err = IpPort(str_server_port, proto, &server_port);
940                 if (err) {
941                         ret = -1;
942                         goto getout;
943                 }
944         } else {
945                 server_port = 0;
946         }
947
948 /* Check that at least the server address has been defined */
949         if (server_addr.s_addr == 0) {
950                 ret = -1;
951                 goto getout;
952         }
953
954 /* Add to linked list */
955         proxy_entry = malloc(sizeof(struct proxy_entry));
956         if (proxy_entry == NULL) {
957                 ret = -1;
958                 goto getout;
959         }
960
961         proxy_entry->proxy_type = proxy_type;
962         proxy_entry->rule_index = rule_index;
963         proxy_entry->proto = proto;
964         proxy_entry->proxy_port = htons(proxy_port);
965         proxy_entry->server_port = htons(server_port);
966         proxy_entry->server_addr = server_addr;
967         proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
968         proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
969         proxy_entry->src_mask = src_mask;
970         proxy_entry->dst_mask = dst_mask;
971
972         RuleAdd(la, proxy_entry);
973
974 getout:
975         LIBALIAS_UNLOCK(la);
976         return (ret);
977 }