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