sort(1): Fix some obvious issues
[dragonfly.git] / lib / libalias / alias_proxy.c
blob23e135ca6ac3913fa45389fc621f471d45cdf17a
1 /*-
2 * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/lib/libalias/alias_proxy.c,v 1.4.2.5 2001/11/03 11:34:33 brian Exp $
27 * $DragonFly: src/lib/libalias/alias_proxy.c,v 1.3 2004/08/20 00:08:17 joerg Exp $
30 /* file: alias_proxy.c
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.
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 a of tcp stream, or inclusion of an optional field
41 in the IP header.
43 There is one public API function:
45 PacketAliasProxyRule() -- Adds and deletes proxy
46 rules.
48 Rules are stored in a linear linked list, so lookup efficiency
49 won't be too good for large lists.
52 Initial development: April, 1998 (cjm)
56 /* System includes */
57 #include <sys/param.h>
58 #include <sys/socket.h>
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <netdb.h>
66 /* BSD IPV4 includes */
67 #include <netinet/in_systm.h>
68 #include <netinet/in.h>
69 #include <netinet/ip.h>
70 #include <netinet/tcp.h>
72 #include <arpa/inet.h>
74 #include "alias_local.h" /* Functions used by alias*.c */
75 #include "alias.h" /* Public API functions for libalias */
80 Data structures
84 * A linked list of arbitrary length, based on struct proxy_entry is
85 * used to store proxy rules.
87 struct proxy_entry
89 #define PROXY_TYPE_ENCODE_NONE 1
90 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
91 #define PROXY_TYPE_ENCODE_IPHDR 3
92 int rule_index;
93 int proxy_type;
94 u_char proto;
95 u_short proxy_port;
96 u_short server_port;
98 struct in_addr server_addr;
100 struct in_addr src_addr;
101 struct in_addr src_mask;
103 struct in_addr dst_addr;
104 struct in_addr dst_mask;
106 struct proxy_entry *next;
107 struct proxy_entry *last;
113 File scope variables
116 static struct proxy_entry *proxyList;
120 /* Local (static) functions:
122 IpMask() -- Utility function for creating IP
123 masks from integer (1-32) specification.
124 IpAddr() -- Utility function for converting string
125 to IP address
126 IpPort() -- Utility function for converting string
127 to port number
128 RuleAdd() -- Adds an element to the rule list.
129 RuleDelete() -- Removes an element from the rule list.
130 RuleNumberDelete() -- Removes all elements from the rule list
131 having a certain rule number.
132 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
133 of a TCP stream.
134 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
135 destination of a proxied IP packet
138 static int IpMask(int, struct in_addr *);
139 static int IpAddr(char *, struct in_addr *);
140 static int IpPort(char *, int, int *);
141 static void RuleAdd(struct proxy_entry *);
142 static void RuleDelete(struct proxy_entry *);
143 static int RuleNumberDelete(int);
144 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
145 static void ProxyEncodeIpHeader(struct ip *, int);
147 static int
148 IpMask(int nbits, struct in_addr *mask)
150 int i;
151 u_int imask;
153 if (nbits < 0 || nbits > 32)
154 return -1;
156 imask = 0;
157 for (i=0; i<nbits; i++)
158 imask = (imask >> 1) + 0x80000000;
159 mask->s_addr = htonl(imask);
161 return 0;
164 static int
165 IpAddr(char *s, struct in_addr *addr)
167 if (inet_aton(s, addr) == 0)
168 return -1;
169 else
170 return 0;
173 static int
174 IpPort(char *s, int proto, int *port)
176 int n;
178 n = sscanf(s, "%d", port);
179 if (n != 1)
181 struct servent *se;
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;
190 if (se == NULL)
191 return -1;
193 *port = (u_int) ntohs(se->s_port);
196 return 0;
199 void
200 RuleAdd(struct proxy_entry *entry)
202 int rule_index;
203 struct proxy_entry *ptr;
204 struct proxy_entry *ptr_last;
206 if (proxyList == NULL)
208 proxyList = entry;
209 entry->last = NULL;
210 entry->next = NULL;
211 return;
214 rule_index = entry->rule_index;
215 ptr = proxyList;
216 ptr_last = NULL;
217 while (ptr != NULL)
219 if (ptr->rule_index >= rule_index)
221 if (ptr_last == NULL)
223 entry->next = proxyList;
224 entry->last = NULL;
225 proxyList->last = entry;
226 proxyList = entry;
227 return;
230 ptr_last->next = entry;
231 ptr->last = entry;
232 entry->last = ptr->last;
233 entry->next = ptr;
234 return;
236 ptr_last = ptr;
237 ptr = ptr->next;
240 ptr_last->next = entry;
241 entry->last = ptr_last;
242 entry->next = NULL;
245 static void
246 RuleDelete(struct proxy_entry *entry)
248 if (entry->last != NULL)
249 entry->last->next = entry->next;
250 else
251 proxyList = entry->next;
253 if (entry->next != NULL)
254 entry->next->last = entry->last;
256 free(entry);
259 static int
260 RuleNumberDelete(int rule_index)
262 int err;
263 struct proxy_entry *ptr;
265 err = -1;
266 ptr = proxyList;
267 while (ptr != NULL)
269 struct proxy_entry *ptr_next;
271 ptr_next = ptr->next;
272 if (ptr->rule_index == rule_index)
274 err = 0;
275 RuleDelete(ptr);
278 ptr = ptr_next;
281 return err;
284 static void
285 ProxyEncodeTcpStream(struct alias_link *link,
286 struct ip *pip,
287 int maxpacketsize)
289 int slen;
290 char buffer[40];
291 struct tcphdr *tc;
293 /* Compute pointer to tcp header */
294 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
296 /* Don't modify if once already modified */
298 if (GetAckModified (link))
299 return;
301 /* Translate destination address and port to string form */
302 snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
303 inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link)));
305 /* Pad string out to a multiple of two in length */
306 slen = strlen(buffer);
307 switch (slen % 2)
309 case 0:
310 strcat(buffer, " \n");
311 slen += 2;
312 break;
313 case 1:
314 strcat(buffer, "\n");
315 slen += 1;
318 /* Check for packet overflow */
319 if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
320 return;
322 /* Shift existing TCP data and insert destination string */
324 int dlen;
325 int hlen;
326 u_char *p;
328 hlen = (pip->ip_hl + tc->th_off) << 2;
329 dlen = ntohs (pip->ip_len) - hlen;
331 /* Modify first packet that has data in it */
333 if (dlen == 0)
334 return;
336 p = (char *) pip;
337 p += hlen;
339 memmove(p + slen, p, dlen);
340 memcpy(p, buffer, slen);
343 /* Save information about modfied sequence number */
345 int delta;
347 SetAckModified(link);
348 delta = GetDeltaSeqOut(pip, link);
349 AddSeq(pip, link, delta+slen);
352 /* Update IP header packet length and checksum */
354 int accumulate;
356 accumulate = pip->ip_len;
357 pip->ip_len = htons(ntohs(pip->ip_len) + slen);
358 accumulate -= pip->ip_len;
360 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
363 /* Update TCP checksum, Use TcpChecksum since so many things have
364 already changed. */
366 tc->th_sum = 0;
367 tc->th_sum = TcpChecksum (pip);
370 static void
371 ProxyEncodeIpHeader(struct ip *pip,
372 int maxpacketsize)
374 #define OPTION_LEN_BYTES 8
375 #define OPTION_LEN_INT16 4
376 #define OPTION_LEN_INT32 2
377 u_char option[OPTION_LEN_BYTES];
379 #ifdef DEBUG
380 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
381 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
382 #endif
384 /* Check to see that there is room to add an IP option */
385 if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
386 return;
388 /* Build option and copy into packet */
390 u_char *ptr;
391 struct tcphdr *tc;
393 ptr = (u_char *) pip;
394 ptr += 20;
395 memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
397 option[0] = 0x64; /* class: 3 (reserved), option 4 */
398 option[1] = OPTION_LEN_BYTES;
400 memcpy(&option[2], (u_char *) &pip->ip_dst, 4);
402 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
403 memcpy(&option[6], (u_char *) &tc->th_sport, 2);
405 memcpy(ptr, option, 8);
408 /* Update checksum, header length and packet length */
410 int i;
411 int accumulate;
412 u_short *sptr;
414 sptr = (u_short *) option;
415 accumulate = 0;
416 for (i=0; i<OPTION_LEN_INT16; i++)
417 accumulate -= *(sptr++);
419 sptr = (u_short *) pip;
420 accumulate += *sptr;
421 pip->ip_hl += OPTION_LEN_INT32;
422 accumulate -= *sptr;
424 accumulate += pip->ip_len;
425 pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
426 accumulate -= pip->ip_len;
428 ADJUST_CHECKSUM(accumulate, pip->ip_sum);
430 #undef OPTION_LEN_BYTES
431 #undef OPTION_LEN_INT16
432 #undef OPTION_LEN_INT32
433 #ifdef DEBUG
434 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
435 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
436 #endif
440 /* Functions by other packet alias source files
442 ProxyCheck() -- Checks whether an outgoing packet should
443 be proxied.
444 ProxyModify() -- Encodes the original destination address/port
445 for a packet which is to be redirected to
446 a proxy server.
450 ProxyCheck(struct ip *pip,
451 struct in_addr *proxy_server_addr,
452 u_short *proxy_server_port)
454 u_short dst_port;
455 struct in_addr src_addr;
456 struct in_addr dst_addr;
457 struct proxy_entry *ptr;
459 src_addr = pip->ip_src;
460 dst_addr = pip->ip_dst;
461 dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)))
462 ->th_dport;
464 ptr = proxyList;
465 while (ptr != NULL)
467 u_short proxy_port;
469 proxy_port = ptr->proxy_port;
470 if ((dst_port == proxy_port || proxy_port == 0)
471 && pip->ip_p == ptr->proto
472 && src_addr.s_addr != ptr->server_addr.s_addr)
474 struct in_addr src_addr_masked;
475 struct in_addr dst_addr_masked;
477 src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
478 dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
480 if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
481 && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr))
483 if ((*proxy_server_port = ptr->server_port) == 0)
484 *proxy_server_port = dst_port;
485 *proxy_server_addr = ptr->server_addr;
486 return ptr->proxy_type;
489 ptr = ptr->next;
492 return 0;
495 void
496 ProxyModify(struct alias_link *link,
497 struct ip *pip,
498 int maxpacketsize,
499 int proxy_type)
501 switch (proxy_type)
503 case PROXY_TYPE_ENCODE_IPHDR:
504 ProxyEncodeIpHeader(pip, maxpacketsize);
505 break;
507 case PROXY_TYPE_ENCODE_TCPSTREAM:
508 ProxyEncodeTcpStream(link, pip, maxpacketsize);
509 break;
515 Public API functions
519 PacketAliasProxyRule(const char *cmd)
522 * This function takes command strings of the form:
524 * server <addr>[:<port>]
525 * [port <port>]
526 * [rule n]
527 * [proto tcp|udp]
528 * [src <addr>[/n]]
529 * [dst <addr>[/n]]
530 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
532 * delete <rule number>
534 * Subfields can be in arbitrary order. Port numbers and addresses
535 * must be in either numeric or symbolic form. An optional rule number
536 * is used to control the order in which rules are searched. If two
537 * rules have the same number, then search order cannot be guaranteed,
538 * and the rules should be disjoint. If no rule number is specified,
539 * then 0 is used, and group 0 rules are always checked before any
540 * others.
542 int i, n, len;
543 int cmd_len;
544 int token_count;
545 int state;
546 char *token;
547 char buffer[256];
548 char str_port[sizeof(buffer)];
549 char str_server_port[sizeof(buffer)];
550 char *res = buffer;
552 int rule_index;
553 int proto;
554 int proxy_type;
555 int proxy_port;
556 int server_port;
557 struct in_addr server_addr;
558 struct in_addr src_addr, src_mask;
559 struct in_addr dst_addr, dst_mask;
560 struct proxy_entry *proxy_entry;
562 /* Copy command line into a buffer */
563 cmd += strspn(cmd, " \t");
564 cmd_len = strlen(cmd);
565 if (cmd_len > (sizeof(buffer) - 1))
566 return -1;
567 strcpy(buffer, cmd);
569 /* Convert to lower case */
570 len = strlen(buffer);
571 for (i=0; i<len; i++)
572 buffer[i] = tolower((unsigned char)buffer[i]);
574 /* Set default proxy type */
576 /* Set up default values */
577 rule_index = 0;
578 proxy_type = PROXY_TYPE_ENCODE_NONE;
579 proto = IPPROTO_TCP;
580 proxy_port = 0;
581 server_addr.s_addr = 0;
582 server_port = 0;
583 src_addr.s_addr = 0;
584 IpMask(0, &src_mask);
585 dst_addr.s_addr = 0;
586 IpMask(0, &dst_mask);
588 str_port[0] = 0;
589 str_server_port[0] = 0;
591 /* Parse command string with state machine */
592 #define STATE_READ_KEYWORD 0
593 #define STATE_READ_TYPE 1
594 #define STATE_READ_PORT 2
595 #define STATE_READ_SERVER 3
596 #define STATE_READ_RULE 4
597 #define STATE_READ_DELETE 5
598 #define STATE_READ_PROTO 6
599 #define STATE_READ_SRC 7
600 #define STATE_READ_DST 8
601 state = STATE_READ_KEYWORD;
602 token = strsep(&res, " \t");
603 token_count = 0;
604 while (token != NULL)
606 token_count++;
607 switch (state)
609 case STATE_READ_KEYWORD:
610 if (strcmp(token, "type") == 0)
611 state = STATE_READ_TYPE;
612 else if (strcmp(token, "port") == 0)
613 state = STATE_READ_PORT;
614 else if (strcmp(token, "server") == 0)
615 state = STATE_READ_SERVER;
616 else if (strcmp(token, "rule") == 0)
617 state = STATE_READ_RULE;
618 else if (strcmp(token, "delete") == 0)
619 state = STATE_READ_DELETE;
620 else if (strcmp(token, "proto") == 0)
621 state = STATE_READ_PROTO;
622 else if (strcmp(token, "src") == 0)
623 state = STATE_READ_SRC;
624 else if (strcmp(token, "dst") == 0)
625 state = STATE_READ_DST;
626 else
627 return -1;
628 break;
630 case STATE_READ_TYPE:
631 if (strcmp(token, "encode_ip_hdr") == 0)
632 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
633 else if (strcmp(token, "encode_tcp_stream") == 0)
634 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
635 else if (strcmp(token, "no_encode") == 0)
636 proxy_type = PROXY_TYPE_ENCODE_NONE;
637 else
638 return -1;
639 state = STATE_READ_KEYWORD;
640 break;
642 case STATE_READ_PORT:
643 strcpy(str_port, token);
644 state = STATE_READ_KEYWORD;
645 break;
647 case STATE_READ_SERVER:
649 int err;
650 char *p;
651 char s[sizeof(buffer)];
653 p = token;
654 while (*p != ':' && *p != 0)
655 p++;
657 if (*p != ':')
659 err = IpAddr(token, &server_addr);
660 if (err)
661 return -1;
663 else
665 *p = ' ';
667 n = sscanf(token, "%s %s", s, str_server_port);
668 if (n != 2)
669 return -1;
671 err = IpAddr(s, &server_addr);
672 if (err)
673 return -1;
676 state = STATE_READ_KEYWORD;
677 break;
679 case STATE_READ_RULE:
680 n = sscanf(token, "%d", &rule_index);
681 if (n != 1 || rule_index < 0)
682 return -1;
683 state = STATE_READ_KEYWORD;
684 break;
686 case STATE_READ_DELETE:
688 int err;
689 int rule_to_delete;
691 if (token_count != 2)
692 return -1;
694 n = sscanf(token, "%d", &rule_to_delete);
695 if (n != 1)
696 return -1;
697 err = RuleNumberDelete(rule_to_delete);
698 if (err)
699 return -1;
700 return 0;
703 case STATE_READ_PROTO:
704 if (strcmp(token, "tcp") == 0)
705 proto = IPPROTO_TCP;
706 else if (strcmp(token, "udp") == 0)
707 proto = IPPROTO_UDP;
708 else
709 return -1;
710 state = STATE_READ_KEYWORD;
711 break;
713 case STATE_READ_SRC:
714 case STATE_READ_DST:
716 int err;
717 char *p;
718 struct in_addr mask;
719 struct in_addr addr;
721 p = token;
722 while (*p != '/' && *p != 0)
723 p++;
725 if (*p != '/')
727 IpMask(32, &mask);
728 err = IpAddr(token, &addr);
729 if (err)
730 return -1;
732 else
734 int nbits;
735 char s[sizeof(buffer)];
737 *p = ' ';
738 n = sscanf(token, "%s %d", s, &nbits);
739 if (n != 2)
740 return -1;
742 err = IpAddr(s, &addr);
743 if (err)
744 return -1;
746 err = IpMask(nbits, &mask);
747 if (err)
748 return -1;
751 if (state == STATE_READ_SRC)
753 src_addr = addr;
754 src_mask = mask;
756 else
758 dst_addr = addr;
759 dst_mask = mask;
762 state = STATE_READ_KEYWORD;
763 break;
765 default:
766 return -1;
767 break;
770 do {
771 token = strsep(&res, " \t");
772 } while (token != NULL && !*token);
774 #undef STATE_READ_KEYWORD
775 #undef STATE_READ_TYPE
776 #undef STATE_READ_PORT
777 #undef STATE_READ_SERVER
778 #undef STATE_READ_RULE
779 #undef STATE_READ_DELETE
780 #undef STATE_READ_PROTO
781 #undef STATE_READ_SRC
782 #undef STATE_READ_DST
784 /* Convert port strings to numbers. This needs to be done after
785 the string is parsed, because the prototype might not be designated
786 before the ports (which might be symbolic entries in /etc/services) */
788 if (strlen(str_port) != 0)
790 int err;
792 err = IpPort(str_port, proto, &proxy_port);
793 if (err)
794 return -1;
796 else
798 proxy_port = 0;
801 if (strlen(str_server_port) != 0)
803 int err;
805 err = IpPort(str_server_port, proto, &server_port);
806 if (err)
807 return -1;
809 else
811 server_port = 0;
814 /* Check that at least the server address has been defined */
815 if (server_addr.s_addr == 0)
816 return -1;
818 /* Add to linked list */
819 proxy_entry = malloc(sizeof(struct proxy_entry));
820 if (proxy_entry == NULL)
821 return -1;
823 proxy_entry->proxy_type = proxy_type;
824 proxy_entry->rule_index = rule_index;
825 proxy_entry->proto = proto;
826 proxy_entry->proxy_port = htons(proxy_port);
827 proxy_entry->server_port = htons(server_port);
828 proxy_entry->server_addr = server_addr;
829 proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
830 proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
831 proxy_entry->src_mask = src_mask;
832 proxy_entry->dst_mask = dst_mask;
834 RuleAdd(proxy_entry);
836 return 0;