2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
25 * All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * $FreeBSD: src/lib/libalias/alias_proxy.c,v 1.4.2.4 2001/08/01 09:52:27 obrien Exp $
52 /* file: alias_proxy.c
54 This file encapsulates special operations related to transparent
55 proxy redirection. This is where packets with a particular destination,
56 usually tcp port 80, are redirected to a proxy server.
58 When packets are proxied, the destination address and port are
59 modified. In certain cases, it is necessary to somehow encode
60 the original address/port info into the packet. Two methods are
61 presently supported: addition of a [DEST addr port] string at the
62 beginning a of tcp stream, or inclusion of an optional field
65 There is one public API function:
67 PacketAliasProxyRule() -- Adds and deletes proxy
70 Rules are stored in a linear linked list, so lookup efficiency
71 won't be too good for large lists.
74 Initial development: April, 1998 (cjm)
85 #include <sys/types.h>
86 #include <sys/socket.h>
88 /* BSD IPV4 includes */
89 #include <netinet/in_systm.h>
90 #include <netinet/in.h>
91 #include <netinet/ip.h>
92 #include <netinet/tcp.h>
94 #include <arpa/inet.h>
96 #include "alias_local.h" /* Functions used by alias*.c */
97 #include "alias.h" /* Public API functions for libalias */
106 * A linked list of arbitrary length, based on struct proxy_entry is
107 * used to store proxy rules.
111 #define PROXY_TYPE_ENCODE_NONE 1
112 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
113 #define PROXY_TYPE_ENCODE_IPHDR 3
120 struct in_addr server_addr
;
122 struct in_addr src_addr
;
123 struct in_addr src_mask
;
125 struct in_addr dst_addr
;
126 struct in_addr dst_mask
;
128 struct proxy_entry
*next
;
129 struct proxy_entry
*last
;
138 static struct proxy_entry
*proxyList
;
142 /* Local (static) functions:
144 IpMask() -- Utility function for creating IP
145 masks from integer (1-32) specification.
146 IpAddr() -- Utility function for converting string
148 IpPort() -- Utility function for converting string
150 RuleAdd() -- Adds an element to the rule list.
151 RuleDelete() -- Removes an element from the rule list.
152 RuleNumberDelete() -- Removes all elements from the rule list
153 having a certain rule number.
154 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
156 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
157 destination of a proxied IP packet
160 static int IpMask(int, struct in_addr
*);
161 static int IpAddr(char *, struct in_addr
*);
162 static int IpPort(char *, int, int *);
163 static void RuleAdd(struct proxy_entry
*);
164 static void RuleDelete(struct proxy_entry
*);
165 static int RuleNumberDelete(int);
166 static void ProxyEncodeTcpStream(struct alias_link
*, struct ip
*, int);
167 static void ProxyEncodeIpHeader(struct ip
*, int);
170 IpMask(int nbits
, struct in_addr
*mask
)
175 if (nbits
< 0 || nbits
> 32)
179 for (i
=0; i
<nbits
; i
++)
180 imask
= (imask
>> 1) + 0x80000000;
181 mask
->s_addr
= htonl(imask
);
187 IpAddr(char *s
, struct in_addr
*addr
)
189 if (inet_aton(s
, addr
) == 0)
196 IpPort(char *s
, int proto
, int *port
)
200 n
= sscanf(s
, "%d", port
);
205 if (proto
== IPPROTO_TCP
)
206 se
= getservbyname(s
, "tcp");
207 else if (proto
== IPPROTO_UDP
)
208 se
= getservbyname(s
, "udp");
215 *port
= (u_int
) ntohs(se
->s_port
);
222 RuleAdd(struct proxy_entry
*entry
)
225 struct proxy_entry
*ptr
;
226 struct proxy_entry
*ptr_last
;
228 if (proxyList
== NULL
)
236 rule_index
= entry
->rule_index
;
241 if (ptr
->rule_index
>= rule_index
)
243 if (ptr_last
== NULL
)
245 entry
->next
= proxyList
;
247 proxyList
->last
= entry
;
252 ptr_last
->next
= entry
;
254 entry
->last
= ptr
->last
;
262 ptr_last
->next
= entry
;
263 entry
->last
= ptr_last
;
268 RuleDelete(struct proxy_entry
*entry
)
270 if (entry
->last
!= NULL
)
271 entry
->last
->next
= entry
->next
;
273 proxyList
= entry
->next
;
275 if (entry
->next
!= NULL
)
276 entry
->next
->last
= entry
->last
;
282 RuleNumberDelete(int rule_index
)
285 struct proxy_entry
*ptr
;
291 struct proxy_entry
*ptr_next
;
293 ptr_next
= ptr
->next
;
294 if (ptr
->rule_index
== rule_index
)
307 ProxyEncodeTcpStream(struct alias_link
*link
,
315 /* Compute pointer to tcp header */
316 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
318 /* Don't modify if once already modified */
320 if (GetAckModified (link
))
323 /* Translate destination address and port to string form */
324 snprintf(buffer
, sizeof(buffer
) - 2, "[DEST %s %d]",
325 inet_ntoa(GetProxyAddress (link
)), (u_int
) ntohs(GetProxyPort (link
)));
327 /* Pad string out to a multiple of two in length */
328 slen
= strlen(buffer
);
332 strcat(buffer
, " \n");
336 strcat(buffer
, "\n");
340 /* Check for packet overflow */
341 if ((ntohs(pip
->ip_len
) + strlen(buffer
)) > maxpacketsize
)
344 /* Shift existing TCP data and insert destination string */
350 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
351 dlen
= ntohs (pip
->ip_len
) - hlen
;
353 /* Modify first packet that has data in it */
361 memmove(p
+ slen
, p
, dlen
);
362 memcpy(p
, buffer
, slen
);
365 /* Save information about modfied sequence number */
369 SetAckModified(link
);
370 delta
= GetDeltaSeqOut(pip
, link
);
371 AddSeq(pip
, link
, delta
+slen
);
374 /* Update IP header packet length and checksum */
378 accumulate
= pip
->ip_len
;
379 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + slen
);
380 accumulate
-= pip
->ip_len
;
382 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
385 /* Update TCP checksum, Use TcpChecksum since so many things have
389 tc
->th_sum
= TcpChecksum (pip
);
393 ProxyEncodeIpHeader(struct ip
*pip
,
396 #define OPTION_LEN_BYTES 8
397 #define OPTION_LEN_INT16 4
398 #define OPTION_LEN_INT32 2
399 u_char option
[OPTION_LEN_BYTES
];
402 fprintf(stdout
, " ip cksum 1 = %x\n", (u_int
) IpChecksum(pip
));
403 fprintf(stdout
, "tcp cksum 1 = %x\n", (u_int
) TcpChecksum(pip
));
406 /* Check to see that there is room to add an IP option */
407 if (pip
->ip_hl
> (0x0f - OPTION_LEN_INT32
))
410 /* Build option and copy into packet */
415 ptr
= (u_char
*) pip
;
417 memcpy(ptr
+ OPTION_LEN_BYTES
, ptr
, ntohs(pip
->ip_len
) - 20);
419 option
[0] = 0x64; /* class: 3 (reserved), option 4 */
420 option
[1] = OPTION_LEN_BYTES
;
422 memcpy(&option
[2], (u_char
*) &pip
->ip_dst
, 4);
424 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
425 memcpy(&option
[6], (u_char
*) &tc
->th_sport
, 2);
427 memcpy(ptr
, option
, 8);
430 /* Update checksum, header length and packet length */
436 sptr
= (u_short
*) option
;
438 for (i
=0; i
<OPTION_LEN_INT16
; i
++)
439 accumulate
-= *(sptr
++);
441 sptr
= (u_short
*) pip
;
443 pip
->ip_hl
+= OPTION_LEN_INT32
;
446 accumulate
+= pip
->ip_len
;
447 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + OPTION_LEN_BYTES
);
448 accumulate
-= pip
->ip_len
;
450 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
452 #undef OPTION_LEN_BYTES
453 #undef OPTION_LEN_INT16
454 #undef OPTION_LEN_INT32
456 fprintf(stdout
, " ip cksum 2 = %x\n", (u_int
) IpChecksum(pip
));
457 fprintf(stdout
, "tcp cksum 2 = %x\n", (u_int
) TcpChecksum(pip
));
462 /* Functions by other packet alias source files
464 ProxyCheck() -- Checks whether an outgoing packet should
466 ProxyModify() -- Encodes the original destination address/port
467 for a packet which is to be redirected to
472 ProxyCheck(struct ip
*pip
,
473 struct in_addr
*proxy_server_addr
,
474 u_short
*proxy_server_port
)
477 struct in_addr src_addr
;
478 struct in_addr dst_addr
;
479 struct proxy_entry
*ptr
;
481 src_addr
= pip
->ip_src
;
482 dst_addr
= pip
->ip_dst
;
483 dst_port
= ((struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2)))
491 proxy_port
= ptr
->proxy_port
;
492 if ((dst_port
== proxy_port
|| proxy_port
== 0)
493 && pip
->ip_p
== ptr
->proto
494 && src_addr
.s_addr
!= ptr
->server_addr
.s_addr
)
496 struct in_addr src_addr_masked
;
497 struct in_addr dst_addr_masked
;
499 src_addr_masked
.s_addr
= src_addr
.s_addr
& ptr
->src_mask
.s_addr
;
500 dst_addr_masked
.s_addr
= dst_addr
.s_addr
& ptr
->dst_mask
.s_addr
;
502 if ((src_addr_masked
.s_addr
== ptr
->src_addr
.s_addr
)
503 && (dst_addr_masked
.s_addr
== ptr
->dst_addr
.s_addr
))
505 if ((*proxy_server_port
= ptr
->server_port
) == 0)
506 *proxy_server_port
= dst_port
;
507 *proxy_server_addr
= ptr
->server_addr
;
508 return ptr
->proxy_type
;
518 ProxyModify(struct alias_link
*link
,
525 case PROXY_TYPE_ENCODE_IPHDR
:
526 ProxyEncodeIpHeader(pip
, maxpacketsize
);
529 case PROXY_TYPE_ENCODE_TCPSTREAM
:
530 ProxyEncodeTcpStream(link
, pip
, maxpacketsize
);
541 PacketAliasProxyRule(const char *cmd
)
544 * This function takes command strings of the form:
546 * server <addr>[:<port>]
552 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
554 * delete <rule number>
556 * Subfields can be in arbitrary order. Port numbers and addresses
557 * must be in either numeric or symbolic form. An optional rule number
558 * is used to control the order in which rules are searched. If two
559 * rules have the same number, then search order cannot be guaranteed,
560 * and the rules should be disjoint. If no rule number is specified,
561 * then 0 is used, and group 0 rules are always checked before any
570 char str_port
[sizeof(buffer
)];
571 char str_server_port
[sizeof(buffer
)];
579 struct in_addr server_addr
;
580 struct in_addr src_addr
, src_mask
;
581 struct in_addr dst_addr
, dst_mask
;
582 struct proxy_entry
*proxy_entry
;
584 /* Copy command line into a buffer */
585 cmd
+= strspn(cmd
, " \t");
586 cmd_len
= strlen(cmd
);
587 if (cmd_len
> (sizeof(buffer
) - 1))
591 /* Convert to lower case */
592 len
= strlen(buffer
);
593 for (i
=0; i
<len
; i
++)
594 buffer
[i
] = tolower((unsigned char)buffer
[i
]);
596 /* Set default proxy type */
598 /* Set up default values */
600 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
603 server_addr
.s_addr
= 0;
606 IpMask(0, &src_mask
);
608 IpMask(0, &dst_mask
);
611 str_server_port
[0] = 0;
613 /* Parse command string with state machine */
614 #define STATE_READ_KEYWORD 0
615 #define STATE_READ_TYPE 1
616 #define STATE_READ_PORT 2
617 #define STATE_READ_SERVER 3
618 #define STATE_READ_RULE 4
619 #define STATE_READ_DELETE 5
620 #define STATE_READ_PROTO 6
621 #define STATE_READ_SRC 7
622 #define STATE_READ_DST 8
623 state
= STATE_READ_KEYWORD
;
624 token
= strsep(&res
, " \t");
626 while (token
!= NULL
)
631 case STATE_READ_KEYWORD
:
632 if (strcmp(token
, "type") == 0)
633 state
= STATE_READ_TYPE
;
634 else if (strcmp(token
, "port") == 0)
635 state
= STATE_READ_PORT
;
636 else if (strcmp(token
, "server") == 0)
637 state
= STATE_READ_SERVER
;
638 else if (strcmp(token
, "rule") == 0)
639 state
= STATE_READ_RULE
;
640 else if (strcmp(token
, "delete") == 0)
641 state
= STATE_READ_DELETE
;
642 else if (strcmp(token
, "proto") == 0)
643 state
= STATE_READ_PROTO
;
644 else if (strcmp(token
, "src") == 0)
645 state
= STATE_READ_SRC
;
646 else if (strcmp(token
, "dst") == 0)
647 state
= STATE_READ_DST
;
652 case STATE_READ_TYPE
:
653 if (strcmp(token
, "encode_ip_hdr") == 0)
654 proxy_type
= PROXY_TYPE_ENCODE_IPHDR
;
655 else if (strcmp(token
, "encode_tcp_stream") == 0)
656 proxy_type
= PROXY_TYPE_ENCODE_TCPSTREAM
;
657 else if (strcmp(token
, "no_encode") == 0)
658 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
661 state
= STATE_READ_KEYWORD
;
664 case STATE_READ_PORT
:
665 strcpy(str_port
, token
);
666 state
= STATE_READ_KEYWORD
;
669 case STATE_READ_SERVER
:
673 char s
[sizeof(buffer
)];
676 while (*p
!= ':' && *p
!= 0)
681 err
= IpAddr(token
, &server_addr
);
689 n
= sscanf(token
, "%s %s", s
, str_server_port
);
693 err
= IpAddr(s
, &server_addr
);
698 state
= STATE_READ_KEYWORD
;
701 case STATE_READ_RULE
:
702 n
= sscanf(token
, "%d", &rule_index
);
703 if (n
!= 1 || rule_index
< 0)
705 state
= STATE_READ_KEYWORD
;
708 case STATE_READ_DELETE
:
713 if (token_count
!= 2)
716 n
= sscanf(token
, "%d", &rule_to_delete
);
719 err
= RuleNumberDelete(rule_to_delete
);
725 case STATE_READ_PROTO
:
726 if (strcmp(token
, "tcp") == 0)
728 else if (strcmp(token
, "udp") == 0)
732 state
= STATE_READ_KEYWORD
;
744 while (*p
!= '/' && *p
!= 0)
750 err
= IpAddr(token
, &addr
);
757 char s
[sizeof(buffer
)];
760 n
= sscanf(token
, "%s %d", s
, &nbits
);
764 err
= IpAddr(s
, &addr
);
768 err
= IpMask(nbits
, &mask
);
773 if (state
== STATE_READ_SRC
)
784 state
= STATE_READ_KEYWORD
;
793 token
= strsep(&res
, " \t");
794 } while (token
!= NULL
&& !*token
);
796 #undef STATE_READ_KEYWORD
797 #undef STATE_READ_TYPE
798 #undef STATE_READ_PORT
799 #undef STATE_READ_SERVER
800 #undef STATE_READ_RULE
801 #undef STATE_READ_DELETE
802 #undef STATE_READ_PROTO
803 #undef STATE_READ_SRC
804 #undef STATE_READ_DST
806 /* Convert port strings to numbers. This needs to be done after
807 the string is parsed, because the prototype might not be designated
808 before the ports (which might be symbolic entries in /etc/services) */
810 if (strlen(str_port
) != 0)
814 err
= IpPort(str_port
, proto
, &proxy_port
);
823 if (strlen(str_server_port
) != 0)
827 err
= IpPort(str_server_port
, proto
, &server_port
);
836 /* Check that at least the server address has been defined */
837 if (server_addr
.s_addr
== 0)
840 /* Add to linked list */
841 proxy_entry
= malloc(sizeof(struct proxy_entry
));
842 if (proxy_entry
== NULL
)
845 proxy_entry
->proxy_type
= proxy_type
;
846 proxy_entry
->rule_index
= rule_index
;
847 proxy_entry
->proto
= proto
;
848 proxy_entry
->proxy_port
= htons(proxy_port
);
849 proxy_entry
->server_port
= htons(server_port
);
850 proxy_entry
->server_addr
= server_addr
;
851 proxy_entry
->src_addr
.s_addr
= src_addr
.s_addr
& src_mask
.s_addr
;
852 proxy_entry
->dst_addr
.s_addr
= dst_addr
.s_addr
& dst_mask
.s_addr
;
853 proxy_entry
->src_mask
= src_mask
;
854 proxy_entry
->dst_mask
= dst_mask
;
856 RuleAdd(proxy_entry
);