2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
24 * All rights reserved.
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
35 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * $FreeBSD: src/lib/libalias/alias_proxy.c,v 1.4.2.4 2001/08/01 09:52:27 obrien Exp $
51 /* file: alias_proxy.c
53 This file encapsulates special operations related to transparent
54 proxy redirection. This is where packets with a particular destination,
55 usually tcp port 80, are redirected to a proxy server.
57 When packets are proxied, the destination address and port are
58 modified. In certain cases, it is necessary to somehow encode
59 the original address/port info into the packet. Two methods are
60 presently supported: addition of a [DEST addr port] string at the
61 beginning a of tcp stream, or inclusion of an optional field
64 There is one public API function:
66 PacketAliasProxyRule() -- Adds and deletes proxy
69 Rules are stored in a linear linked list, so lookup efficiency
70 won't be too good for large lists.
73 Initial development: April, 1998 (cjm)
84 #include <sys/types.h>
85 #include <sys/socket.h>
87 /* BSD IPV4 includes */
88 #include <netinet/in_systm.h>
89 #include <netinet/in.h>
90 #include <netinet/ip.h>
91 #include <netinet/tcp.h>
93 #include <arpa/inet.h>
95 #include "alias_local.h" /* Functions used by alias*.c */
96 #include "alias.h" /* Public API functions for libalias */
105 * A linked list of arbitrary length, based on struct proxy_entry is
106 * used to store proxy rules.
110 #define PROXY_TYPE_ENCODE_NONE 1
111 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
112 #define PROXY_TYPE_ENCODE_IPHDR 3
119 struct in_addr server_addr
;
121 struct in_addr src_addr
;
122 struct in_addr src_mask
;
124 struct in_addr dst_addr
;
125 struct in_addr dst_mask
;
127 struct proxy_entry
*next
;
128 struct proxy_entry
*last
;
137 static struct proxy_entry
*proxyList
;
141 /* Local (static) functions:
143 IpMask() -- Utility function for creating IP
144 masks from integer (1-32) specification.
145 IpAddr() -- Utility function for converting string
147 IpPort() -- Utility function for converting string
149 RuleAdd() -- Adds an element to the rule list.
150 RuleDelete() -- Removes an element from the rule list.
151 RuleNumberDelete() -- Removes all elements from the rule list
152 having a certain rule number.
153 ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
155 ProxyEncodeIpHeader() -- Adds an IP option indicating the true
156 destination of a proxied IP packet
159 static int IpMask(int, struct in_addr
*);
160 static int IpAddr(char *, struct in_addr
*);
161 static int IpPort(char *, int, int *);
162 static void RuleAdd(struct proxy_entry
*);
163 static void RuleDelete(struct proxy_entry
*);
164 static int RuleNumberDelete(int);
165 static void ProxyEncodeTcpStream(struct alias_link
*, struct ip
*, int);
166 static void ProxyEncodeIpHeader(struct ip
*, int);
169 IpMask(int nbits
, struct in_addr
*mask
)
174 if (nbits
< 0 || nbits
> 32)
178 for (i
=0; i
<nbits
; i
++)
179 imask
= (imask
>> 1) + 0x80000000;
180 mask
->s_addr
= htonl(imask
);
186 IpAddr(char *s
, struct in_addr
*addr
)
188 if (inet_aton(s
, addr
) == 0)
195 IpPort(char *s
, int proto
, int *port
)
199 n
= sscanf(s
, "%d", port
);
204 if (proto
== IPPROTO_TCP
)
205 se
= getservbyname(s
, "tcp");
206 else if (proto
== IPPROTO_UDP
)
207 se
= getservbyname(s
, "udp");
214 *port
= (u_int
) ntohs(se
->s_port
);
221 RuleAdd(struct proxy_entry
*entry
)
224 struct proxy_entry
*ptr
;
225 struct proxy_entry
*ptr_last
;
227 if (proxyList
== NULL
)
235 rule_index
= entry
->rule_index
;
240 if (ptr
->rule_index
>= rule_index
)
242 if (ptr_last
== NULL
)
244 entry
->next
= proxyList
;
246 proxyList
->last
= entry
;
251 ptr_last
->next
= entry
;
253 entry
->last
= ptr
->last
;
261 ptr_last
->next
= entry
;
262 entry
->last
= ptr_last
;
267 RuleDelete(struct proxy_entry
*entry
)
269 if (entry
->last
!= NULL
)
270 entry
->last
->next
= entry
->next
;
272 proxyList
= entry
->next
;
274 if (entry
->next
!= NULL
)
275 entry
->next
->last
= entry
->last
;
281 RuleNumberDelete(int rule_index
)
284 struct proxy_entry
*ptr
;
290 struct proxy_entry
*ptr_next
;
292 ptr_next
= ptr
->next
;
293 if (ptr
->rule_index
== rule_index
)
306 ProxyEncodeTcpStream(struct alias_link
*link
,
314 /* Compute pointer to tcp header */
315 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
317 /* Don't modify if once already modified */
319 if (GetAckModified (link
))
322 /* Translate destination address and port to string form */
323 snprintf(buffer
, sizeof(buffer
) - 2, "[DEST %s %d]",
324 inet_ntoa(GetProxyAddress (link
)), (u_int
) ntohs(GetProxyPort (link
)));
326 /* Pad string out to a multiple of two in length */
327 slen
= strlen(buffer
);
331 strcat(buffer
, " \n");
335 strcat(buffer
, "\n");
339 /* Check for packet overflow */
340 if ((ntohs(pip
->ip_len
) + strlen(buffer
)) > maxpacketsize
)
343 /* Shift existing TCP data and insert destination string */
349 hlen
= (pip
->ip_hl
+ tc
->th_off
) << 2;
350 dlen
= ntohs (pip
->ip_len
) - hlen
;
352 /* Modify first packet that has data in it */
360 memmove(p
+ slen
, p
, dlen
);
361 memcpy(p
, buffer
, slen
);
364 /* Save information about modfied sequence number */
368 SetAckModified(link
);
369 delta
= GetDeltaSeqOut(pip
, link
);
370 AddSeq(pip
, link
, delta
+slen
);
373 /* Update IP header packet length and checksum */
377 accumulate
= pip
->ip_len
;
378 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + slen
);
379 accumulate
-= pip
->ip_len
;
381 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
384 /* Update TCP checksum, Use TcpChecksum since so many things have
388 tc
->th_sum
= TcpChecksum (pip
);
392 ProxyEncodeIpHeader(struct ip
*pip
,
395 #define OPTION_LEN_BYTES 8
396 #define OPTION_LEN_INT16 4
397 #define OPTION_LEN_INT32 2
398 u_char option
[OPTION_LEN_BYTES
];
401 fprintf(stdout
, " ip cksum 1 = %x\n", (u_int
) IpChecksum(pip
));
402 fprintf(stdout
, "tcp cksum 1 = %x\n", (u_int
) TcpChecksum(pip
));
405 /* Check to see that there is room to add an IP option */
406 if (pip
->ip_hl
> (0x0f - OPTION_LEN_INT32
))
409 /* Build option and copy into packet */
414 ptr
= (u_char
*) pip
;
416 memcpy(ptr
+ OPTION_LEN_BYTES
, ptr
, ntohs(pip
->ip_len
) - 20);
418 option
[0] = 0x64; /* class: 3 (reserved), option 4 */
419 option
[1] = OPTION_LEN_BYTES
;
421 memcpy(&option
[2], (u_char
*) &pip
->ip_dst
, 4);
423 tc
= (struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2));
424 memcpy(&option
[6], (u_char
*) &tc
->th_sport
, 2);
426 memcpy(ptr
, option
, 8);
429 /* Update checksum, header length and packet length */
435 sptr
= (u_short
*) option
;
437 for (i
=0; i
<OPTION_LEN_INT16
; i
++)
438 accumulate
-= *(sptr
++);
440 sptr
= (u_short
*) pip
;
442 pip
->ip_hl
+= OPTION_LEN_INT32
;
445 accumulate
+= pip
->ip_len
;
446 pip
->ip_len
= htons(ntohs(pip
->ip_len
) + OPTION_LEN_BYTES
);
447 accumulate
-= pip
->ip_len
;
449 ADJUST_CHECKSUM(accumulate
, pip
->ip_sum
);
451 #undef OPTION_LEN_BYTES
452 #undef OPTION_LEN_INT16
453 #undef OPTION_LEN_INT32
455 fprintf(stdout
, " ip cksum 2 = %x\n", (u_int
) IpChecksum(pip
));
456 fprintf(stdout
, "tcp cksum 2 = %x\n", (u_int
) TcpChecksum(pip
));
461 /* Functions by other packet alias source files
463 ProxyCheck() -- Checks whether an outgoing packet should
465 ProxyModify() -- Encodes the original destination address/port
466 for a packet which is to be redirected to
471 ProxyCheck(struct ip
*pip
,
472 struct in_addr
*proxy_server_addr
,
473 u_short
*proxy_server_port
)
476 struct in_addr src_addr
;
477 struct in_addr dst_addr
;
478 struct proxy_entry
*ptr
;
480 src_addr
= pip
->ip_src
;
481 dst_addr
= pip
->ip_dst
;
482 dst_port
= ((struct tcphdr
*) ((char *) pip
+ (pip
->ip_hl
<< 2)))
490 proxy_port
= ptr
->proxy_port
;
491 if ((dst_port
== proxy_port
|| proxy_port
== 0)
492 && pip
->ip_p
== ptr
->proto
493 && src_addr
.s_addr
!= ptr
->server_addr
.s_addr
)
495 struct in_addr src_addr_masked
;
496 struct in_addr dst_addr_masked
;
498 src_addr_masked
.s_addr
= src_addr
.s_addr
& ptr
->src_mask
.s_addr
;
499 dst_addr_masked
.s_addr
= dst_addr
.s_addr
& ptr
->dst_mask
.s_addr
;
501 if ((src_addr_masked
.s_addr
== ptr
->src_addr
.s_addr
)
502 && (dst_addr_masked
.s_addr
== ptr
->dst_addr
.s_addr
))
504 if ((*proxy_server_port
= ptr
->server_port
) == 0)
505 *proxy_server_port
= dst_port
;
506 *proxy_server_addr
= ptr
->server_addr
;
507 return ptr
->proxy_type
;
517 ProxyModify(struct alias_link
*link
,
524 case PROXY_TYPE_ENCODE_IPHDR
:
525 ProxyEncodeIpHeader(pip
, maxpacketsize
);
528 case PROXY_TYPE_ENCODE_TCPSTREAM
:
529 ProxyEncodeTcpStream(link
, pip
, maxpacketsize
);
540 PacketAliasProxyRule(const char *cmd
)
543 * This function takes command strings of the form:
545 * server <addr>[:<port>]
551 * [type encode_tcp_stream|encode_ip_hdr|no_encode]
553 * delete <rule number>
555 * Subfields can be in arbitrary order. Port numbers and addresses
556 * must be in either numeric or symbolic form. An optional rule number
557 * is used to control the order in which rules are searched. If two
558 * rules have the same number, then search order cannot be guaranteed,
559 * and the rules should be disjoint. If no rule number is specified,
560 * then 0 is used, and group 0 rules are always checked before any
569 char str_port
[sizeof(buffer
)];
570 char str_server_port
[sizeof(buffer
)];
578 struct in_addr server_addr
;
579 struct in_addr src_addr
, src_mask
;
580 struct in_addr dst_addr
, dst_mask
;
581 struct proxy_entry
*proxy_entry
;
583 /* Copy command line into a buffer */
584 cmd
+= strspn(cmd
, " \t");
585 cmd_len
= strlen(cmd
);
586 if (cmd_len
> (sizeof(buffer
) - 1))
590 /* Convert to lower case */
591 len
= strlen(buffer
);
592 for (i
=0; i
<len
; i
++)
593 buffer
[i
] = tolower((unsigned char)buffer
[i
]);
595 /* Set default proxy type */
597 /* Set up default values */
599 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
602 server_addr
.s_addr
= 0;
605 IpMask(0, &src_mask
);
607 IpMask(0, &dst_mask
);
610 str_server_port
[0] = 0;
612 /* Parse command string with state machine */
613 #define STATE_READ_KEYWORD 0
614 #define STATE_READ_TYPE 1
615 #define STATE_READ_PORT 2
616 #define STATE_READ_SERVER 3
617 #define STATE_READ_RULE 4
618 #define STATE_READ_DELETE 5
619 #define STATE_READ_PROTO 6
620 #define STATE_READ_SRC 7
621 #define STATE_READ_DST 8
622 state
= STATE_READ_KEYWORD
;
623 token
= strsep(&res
, " \t");
625 while (token
!= NULL
)
630 case STATE_READ_KEYWORD
:
631 if (strcmp(token
, "type") == 0)
632 state
= STATE_READ_TYPE
;
633 else if (strcmp(token
, "port") == 0)
634 state
= STATE_READ_PORT
;
635 else if (strcmp(token
, "server") == 0)
636 state
= STATE_READ_SERVER
;
637 else if (strcmp(token
, "rule") == 0)
638 state
= STATE_READ_RULE
;
639 else if (strcmp(token
, "delete") == 0)
640 state
= STATE_READ_DELETE
;
641 else if (strcmp(token
, "proto") == 0)
642 state
= STATE_READ_PROTO
;
643 else if (strcmp(token
, "src") == 0)
644 state
= STATE_READ_SRC
;
645 else if (strcmp(token
, "dst") == 0)
646 state
= STATE_READ_DST
;
651 case STATE_READ_TYPE
:
652 if (strcmp(token
, "encode_ip_hdr") == 0)
653 proxy_type
= PROXY_TYPE_ENCODE_IPHDR
;
654 else if (strcmp(token
, "encode_tcp_stream") == 0)
655 proxy_type
= PROXY_TYPE_ENCODE_TCPSTREAM
;
656 else if (strcmp(token
, "no_encode") == 0)
657 proxy_type
= PROXY_TYPE_ENCODE_NONE
;
660 state
= STATE_READ_KEYWORD
;
663 case STATE_READ_PORT
:
664 strcpy(str_port
, token
);
665 state
= STATE_READ_KEYWORD
;
668 case STATE_READ_SERVER
:
672 char s
[sizeof(buffer
)];
675 while (*p
!= ':' && *p
!= 0)
680 err
= IpAddr(token
, &server_addr
);
688 n
= sscanf(token
, "%s %s", s
, str_server_port
);
692 err
= IpAddr(s
, &server_addr
);
697 state
= STATE_READ_KEYWORD
;
700 case STATE_READ_RULE
:
701 n
= sscanf(token
, "%d", &rule_index
);
702 if (n
!= 1 || rule_index
< 0)
704 state
= STATE_READ_KEYWORD
;
707 case STATE_READ_DELETE
:
712 if (token_count
!= 2)
715 n
= sscanf(token
, "%d", &rule_to_delete
);
718 err
= RuleNumberDelete(rule_to_delete
);
724 case STATE_READ_PROTO
:
725 if (strcmp(token
, "tcp") == 0)
727 else if (strcmp(token
, "udp") == 0)
731 state
= STATE_READ_KEYWORD
;
743 while (*p
!= '/' && *p
!= 0)
749 err
= IpAddr(token
, &addr
);
756 char s
[sizeof(buffer
)];
759 n
= sscanf(token
, "%s %d", s
, &nbits
);
763 err
= IpAddr(s
, &addr
);
767 err
= IpMask(nbits
, &mask
);
772 if (state
== STATE_READ_SRC
)
783 state
= STATE_READ_KEYWORD
;
792 token
= strsep(&res
, " \t");
793 } while (token
!= NULL
&& !*token
);
795 #undef STATE_READ_KEYWORD
796 #undef STATE_READ_TYPE
797 #undef STATE_READ_PORT
798 #undef STATE_READ_SERVER
799 #undef STATE_READ_RULE
800 #undef STATE_READ_DELETE
801 #undef STATE_READ_PROTO
802 #undef STATE_READ_SRC
803 #undef STATE_READ_DST
805 /* Convert port strings to numbers. This needs to be done after
806 the string is parsed, because the prototype might not be designated
807 before the ports (which might be symbolic entries in /etc/services) */
809 if (strlen(str_port
) != 0)
813 err
= IpPort(str_port
, proto
, &proxy_port
);
822 if (strlen(str_server_port
) != 0)
826 err
= IpPort(str_server_port
, proto
, &server_port
);
835 /* Check that at least the server address has been defined */
836 if (server_addr
.s_addr
== 0)
839 /* Add to linked list */
840 proxy_entry
= malloc(sizeof(struct proxy_entry
));
841 if (proxy_entry
== NULL
)
844 proxy_entry
->proxy_type
= proxy_type
;
845 proxy_entry
->rule_index
= rule_index
;
846 proxy_entry
->proto
= proto
;
847 proxy_entry
->proxy_port
= htons(proxy_port
);
848 proxy_entry
->server_port
= htons(server_port
);
849 proxy_entry
->server_addr
= server_addr
;
850 proxy_entry
->src_addr
.s_addr
= src_addr
.s_addr
& src_mask
.s_addr
;
851 proxy_entry
->dst_addr
.s_addr
= dst_addr
.s_addr
& dst_mask
.s_addr
;
852 proxy_entry
->src_mask
= src_mask
;
853 proxy_entry
->dst_mask
= dst_mask
;
855 RuleAdd(proxy_entry
);