2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * $FreeBSD: src/lib/libalias/alias_proxy.c,v 1.4.2.4 2001/08/01 09:52:27 obrien Exp $
58 /* file: alias_proxy.c
60 This file encapsulates special operations related to transparent
61 proxy redirection. This is where packets with a particular destination,
62 usually tcp port 80, are redirected to a proxy server.
64 When packets are proxied, the destination address and port are
65 modified. In certain cases, it is necessary to somehow encode
66 the original address/port info into the packet. Two methods are
67 presently supported: addition of a [DEST addr port] string at the
68 beginning a of tcp stream, or inclusion of an optional field
71 There is one public API function:
73 PacketAliasProxyRule() -- Adds and deletes proxy
76 Rules are stored in a linear linked list, so lookup efficiency
77 won't be too good for large lists.
80 Initial development: April, 1998 (cjm)
91 #include <sys/types.h>
92 #include <sys/socket.h>
94 /* BSD IPV4 includes */
95 #include <netinet/in_systm.h>
96 #include <netinet/in.h>
97 #include <netinet/ip.h>
98 #include <netinet/tcp.h>
100 #include <arpa/inet.h>
102 #include "alias_local.h" /* Functions used by alias*.c */
103 #include "alias.h" /* Public API functions for libalias */
112 * A linked list of arbitrary length, based on struct proxy_entry is
113 * used to store proxy rules.
117 #define PROXY_TYPE_ENCODE_NONE 1
118 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
119 #define PROXY_TYPE_ENCODE_IPHDR 3
126 struct in_addr server_addr
;
128 struct in_addr src_addr
;
129 struct in_addr src_mask
;
131 struct in_addr dst_addr
;
132 struct in_addr dst_mask
;
134 struct proxy_entry
*next
;
135 struct proxy_entry
*last
;
144 static struct proxy_entry
*proxyList
;
148 /* Local (static) functions:
150 IpMask() -- Utility function for creating IP
151 masks from integer (1-32) specification.
152 IpAddr() -- Utility function for converting string
154 IpPort() -- Utility function for converting string
156 RuleAdd() -- Adds an element to the rule list.
157 RuleDelete() -- Removes an element from the rule list.
158 RuleNumberDelete() -- Removes all elements from the rule list
159 having a certain rule number.
160 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
162 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
163 destination of a proxied IP packet
166 static int IpMask(int, struct in_addr
*);
167 static int IpAddr(char *, struct in_addr
*);
168 static int IpPort(char *, int, int *);
169 static void RuleAdd(struct proxy_entry
*);
170 static void RuleDelete(struct proxy_entry
*);
171 static int RuleNumberDelete(int);
172 static void ProxyEncodeTcpStream(struct alias_link
*, struct ip
*, int);
173 static void ProxyEncodeIpHeader(struct ip
*, int);
176 IpMask(int nbits
, struct in_addr
*mask
)
181 if (nbits
< 0 || nbits
> 32)
185 for (i
=0; i
<nbits
; i
++)
186 imask
= (imask
>> 1) + 0x80000000;
187 mask
->s_addr
= htonl(imask
);
193 IpAddr(char *s
, struct in_addr
*addr
)
195 if (inet_aton(s
, addr
) == 0)
202 IpPort(char *s
, int proto
, int *port
)
206 n
= sscanf(s
, "%d", port
);
211 if (proto
== IPPROTO_TCP
)
212 se
= getservbyname(s
, "tcp");
213 else if (proto
== IPPROTO_UDP
)
214 se
= getservbyname(s
, "udp");
221 *port
= (u_int
) ntohs(se
->s_port
);
228 RuleAdd(struct proxy_entry
*entry
)
231 struct proxy_entry
*ptr
;
232 struct proxy_entry
*ptr_last
;
234 if (proxyList
== NULL
)
242 rule_index
= entry
->rule_index
;
247 if (ptr
->rule_index
>= rule_index
)
249 if (ptr_last
== NULL
)
251 entry
->next
= proxyList
;
253 proxyList
->last
= entry
;
258 ptr_last
->next
= entry
;
260 entry
->last
= ptr
->last
;
268 ptr_last
->next
= entry
;
269 entry
->last
= ptr_last
;
274 RuleDelete(struct proxy_entry
*entry
)
276 if (entry
->last
!= NULL
)
277 entry
->last
->next
= entry
->next
;
279 proxyList
= entry
->next
;
281 if (entry
->next
!= NULL
)
282 entry
->next
->last
= entry
->last
;
288 RuleNumberDelete(int rule_index
)
291 struct proxy_entry
*ptr
;
297 struct proxy_entry
*ptr_next
;
299 ptr_next
= ptr
->next
;
300 if (ptr
->rule_index
== rule_index
)
313 ProxyEncodeTcpStream(struct alias_link
*link
,
321 /* Compute pointer to tcp header */
322 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
324 /* Don't modify if once already modified */
326 if (GetAckModified (link
))
329 /* Translate destination address and port to string form */
330 snprintf(buffer
, sizeof(buffer
) - 2, "[DEST %s %d]",
331 inet_ntoa(GetProxyAddress (link
)), (u_int
) ntohs(GetProxyPort (link
)));
333 /* Pad string out to a multiple of two in length */
334 slen
= strlen(buffer
);
338 strlcat(buffer
, " \n", sizeof(buffer
));
342 strlcat(buffer
, "\n", sizeof(buffer
));
346 /* Check for packet overflow */
347 if ((ntohs(pip
->ip_len
) + strlen(buffer
)) > maxpacketsize
)
350 /* Shift existing TCP data and insert destination string */
356 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
357 dlen
= ntohs (pip
->ip_len
) - hlen
;
359 /* Modify first packet that has data in it */
367 memmove(p
+ slen
, p
, dlen
);
368 memcpy(p
, buffer
, slen
);
371 /* Save information about modfied sequence number */
375 SetAckModified(link
);
376 delta
= GetDeltaSeqOut(pip
, link
);
377 AddSeq(pip
, link
, delta
+slen
);
380 /* Update IP header packet length and checksum */
384 accumulate
= pip
->ip_len
;
385 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + slen
);
386 accumulate
-= pip
->ip_len
;
388 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
391 /* Update TCP checksum, Use TcpChecksum since so many things have
395 tc
->th_sum
= TcpChecksum (pip
);
399 ProxyEncodeIpHeader(struct ip
*pip
,
402 #define OPTION_LEN_BYTES 8
403 #define OPTION_LEN_INT16 4
404 #define OPTION_LEN_INT32 2
405 u_char option
[OPTION_LEN_BYTES
];
408 fprintf(stdout
, " ip cksum 1 = %x\n", (u_int
) IpChecksum(pip
));
409 fprintf(stdout
, "tcp cksum 1 = %x\n", (u_int
) TcpChecksum(pip
));
412 /* Check to see that there is room to add an IP option */
413 if (pip
->ip_hl
> (0x0f - OPTION_LEN_INT32
))
416 /* Build option and copy into packet */
421 ptr
= (u_char
*) pip
;
423 memcpy(ptr
+ OPTION_LEN_BYTES
, ptr
, ntohs(pip
->ip_len
) - 20);
425 option
[0] = 0x64; /* class: 3 (reserved), option 4 */
426 option
[1] = OPTION_LEN_BYTES
;
428 memcpy(&option
[2], (u_char
*) &pip
->ip_dst
, 4);
430 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
431 memcpy(&option
[6], (u_char
*) &tc
->th_sport
, 2);
433 memcpy(ptr
, option
, 8);
436 /* Update checksum, header length and packet length */
442 sptr
= (u_short
*) option
;
444 for (i
=0; i
<OPTION_LEN_INT16
; i
++)
445 accumulate
-= *(sptr
++);
447 sptr
= (u_short
*) pip
;
449 pip
->ip_hl
+= OPTION_LEN_INT32
;
452 accumulate
+= pip
->ip_len
;
453 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + OPTION_LEN_BYTES
);
454 accumulate
-= pip
->ip_len
;
456 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
458 #undef OPTION_LEN_BYTES
459 #undef OPTION_LEN_INT16
460 #undef OPTION_LEN_INT32
462 fprintf(stdout
, " ip cksum 2 = %x\n", (u_int
) IpChecksum(pip
));
463 fprintf(stdout
, "tcp cksum 2 = %x\n", (u_int
) TcpChecksum(pip
));
468 /* Functions by other packet alias source files
470 ProxyCheck() -- Checks whether an outgoing packet should
472 ProxyModify() -- Encodes the original destination address/port
473 for a packet which is to be redirected to
478 ProxyCheck(struct ip
*pip
,
479 struct in_addr
*proxy_server_addr
,
480 u_short
*proxy_server_port
)
483 struct in_addr src_addr
;
484 struct in_addr dst_addr
;
485 struct proxy_entry
*ptr
;
487 src_addr
= pip
->ip_src
;
488 dst_addr
= pip
->ip_dst
;
489 dst_port
= ((struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2)))
497 proxy_port
= ptr
->proxy_port
;
498 if ((dst_port
== proxy_port
|| proxy_port
== 0)
499 && pip
->ip_p
== ptr
->proto
500 && src_addr
.s_addr
!= ptr
->server_addr
.s_addr
)
502 struct in_addr src_addr_masked
;
503 struct in_addr dst_addr_masked
;
505 src_addr_masked
.s_addr
= src_addr
.s_addr
& ptr
->src_mask
.s_addr
;
506 dst_addr_masked
.s_addr
= dst_addr
.s_addr
& ptr
->dst_mask
.s_addr
;
508 if ((src_addr_masked
.s_addr
== ptr
->src_addr
.s_addr
)
509 && (dst_addr_masked
.s_addr
== ptr
->dst_addr
.s_addr
))
511 if ((*proxy_server_port
= ptr
->server_port
) == 0)
512 *proxy_server_port
= dst_port
;
513 *proxy_server_addr
= ptr
->server_addr
;
514 return ptr
->proxy_type
;
524 ProxyModify(struct alias_link
*link
,
531 case PROXY_TYPE_ENCODE_IPHDR
:
532 ProxyEncodeIpHeader(pip
, maxpacketsize
);
535 case PROXY_TYPE_ENCODE_TCPSTREAM
:
536 ProxyEncodeTcpStream(link
, pip
, maxpacketsize
);
547 PacketAliasProxyRule(const char *cmd
)
550 * This function takes command strings of the form:
552 * server <addr>[:<port>]
558 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
560 * delete <rule number>
562 * Subfields can be in arbitrary order. Port numbers and addresses
563 * must be in either numeric or symbolic form. An optional rule number
564 * is used to control the order in which rules are searched. If two
565 * rules have the same number, then search order cannot be guaranteed,
566 * and the rules should be disjoint. If no rule number is specified,
567 * then 0 is used, and group 0 rules are always checked before any
576 char str_port
[sizeof(buffer
)];
577 char str_server_port
[sizeof(buffer
)];
585 struct in_addr server_addr
;
586 struct in_addr src_addr
, src_mask
;
587 struct in_addr dst_addr
, dst_mask
;
588 struct proxy_entry
*proxy_entry
;
590 /* Copy command line into a buffer */
591 cmd
+= strspn(cmd
, " \t");
592 cmd_len
= strlen(cmd
);
593 if (cmd_len
> (sizeof(buffer
) - 1))
595 strlcpy(buffer
, cmd
, sizeof(buffer
));
597 /* Convert to lower case */
598 len
= strlen(buffer
);
599 for (i
=0; i
<len
; i
++)
600 buffer
[i
] = tolower((unsigned char)buffer
[i
]);
602 /* Set default proxy type */
604 /* Set up default values */
606 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
609 server_addr
.s_addr
= 0;
612 IpMask(0, &src_mask
);
614 IpMask(0, &dst_mask
);
617 str_server_port
[0] = 0;
619 /* Parse command string with state machine */
620 #define STATE_READ_KEYWORD 0
621 #define STATE_READ_TYPE 1
622 #define STATE_READ_PORT 2
623 #define STATE_READ_SERVER 3
624 #define STATE_READ_RULE 4
625 #define STATE_READ_DELETE 5
626 #define STATE_READ_PROTO 6
627 #define STATE_READ_SRC 7
628 #define STATE_READ_DST 8
629 state
= STATE_READ_KEYWORD
;
630 token
= strsep(&res
, " \t");
632 while (token
!= NULL
)
637 case STATE_READ_KEYWORD
:
638 if (strcmp(token
, "type") == 0)
639 state
= STATE_READ_TYPE
;
640 else if (strcmp(token
, "port") == 0)
641 state
= STATE_READ_PORT
;
642 else if (strcmp(token
, "server") == 0)
643 state
= STATE_READ_SERVER
;
644 else if (strcmp(token
, "rule") == 0)
645 state
= STATE_READ_RULE
;
646 else if (strcmp(token
, "delete") == 0)
647 state
= STATE_READ_DELETE
;
648 else if (strcmp(token
, "proto") == 0)
649 state
= STATE_READ_PROTO
;
650 else if (strcmp(token
, "src") == 0)
651 state
= STATE_READ_SRC
;
652 else if (strcmp(token
, "dst") == 0)
653 state
= STATE_READ_DST
;
658 case STATE_READ_TYPE
:
659 if (strcmp(token
, "encode_ip_hdr") == 0)
660 proxy_type
= PROXY_TYPE_ENCODE_IPHDR
;
661 else if (strcmp(token
, "encode_tcp_stream") == 0)
662 proxy_type
= PROXY_TYPE_ENCODE_TCPSTREAM
;
663 else if (strcmp(token
, "no_encode") == 0)
664 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
667 state
= STATE_READ_KEYWORD
;
670 case STATE_READ_PORT
:
671 strlcpy(str_port
, token
, sizeof(str_port
));
672 state
= STATE_READ_KEYWORD
;
675 case STATE_READ_SERVER
:
679 char s
[sizeof(buffer
)];
682 while (*p
!= ':' && *p
!= 0)
687 err
= IpAddr(token
, &server_addr
);
695 n
= sscanf(token
, "%s %s", s
, str_server_port
);
699 err
= IpAddr(s
, &server_addr
);
704 state
= STATE_READ_KEYWORD
;
707 case STATE_READ_RULE
:
708 n
= sscanf(token
, "%d", &rule_index
);
709 if (n
!= 1 || rule_index
< 0)
711 state
= STATE_READ_KEYWORD
;
714 case STATE_READ_DELETE
:
719 if (token_count
!= 2)
722 n
= sscanf(token
, "%d", &rule_to_delete
);
725 err
= RuleNumberDelete(rule_to_delete
);
731 case STATE_READ_PROTO
:
732 if (strcmp(token
, "tcp") == 0)
734 else if (strcmp(token
, "udp") == 0)
738 state
= STATE_READ_KEYWORD
;
750 while (*p
!= '/' && *p
!= 0)
756 err
= IpAddr(token
, &addr
);
763 char s
[sizeof(buffer
)];
766 n
= sscanf(token
, "%s %d", s
, &nbits
);
770 err
= IpAddr(s
, &addr
);
774 err
= IpMask(nbits
, &mask
);
779 if (state
== STATE_READ_SRC
)
790 state
= STATE_READ_KEYWORD
;
799 token
= strsep(&res
, " \t");
800 } while (token
!= NULL
&& !*token
);
802 #undef STATE_READ_KEYWORD
803 #undef STATE_READ_TYPE
804 #undef STATE_READ_PORT
805 #undef STATE_READ_SERVER
806 #undef STATE_READ_RULE
807 #undef STATE_READ_DELETE
808 #undef STATE_READ_PROTO
809 #undef STATE_READ_SRC
810 #undef STATE_READ_DST
812 /* Convert port strings to numbers. This needs to be done after
813 the string is parsed, because the prototype might not be designated
814 before the ports (which might be symbolic entries in /etc/services) */
816 if (strlen(str_port
) != 0)
820 err
= IpPort(str_port
, proto
, &proxy_port
);
829 if (strlen(str_server_port
) != 0)
833 err
= IpPort(str_server_port
, proto
, &server_port
);
842 /* Check that at least the server address has been defined */
843 if (server_addr
.s_addr
== 0)
846 /* Add to linked list */
847 proxy_entry
= malloc(sizeof(struct proxy_entry
));
848 if (proxy_entry
== NULL
)
851 proxy_entry
->proxy_type
= proxy_type
;
852 proxy_entry
->rule_index
= rule_index
;
853 proxy_entry
->proto
= proto
;
854 proxy_entry
->proxy_port
= htons(proxy_port
);
855 proxy_entry
->server_port
= htons(server_port
);
856 proxy_entry
->server_addr
= server_addr
;
857 proxy_entry
->src_addr
.s_addr
= src_addr
.s_addr
& src_mask
.s_addr
;
858 proxy_entry
->dst_addr
.s_addr
= dst_addr
.s_addr
& dst_mask
.s_addr
;
859 proxy_entry
->src_mask
= src_mask
;
860 proxy_entry
->dst_mask
= dst_mask
;
862 RuleAdd(proxy_entry
);