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