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