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