1 /**
2  * Copyright 2010 Christian Liesch
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * @file
19  *
20  * @Author christian liesch <liesch@gmx.ch>
21  *
22  * Implementation of the HTTP Test Tool socks module
23  */
24 
25 /************************************************************************
26  * Includes
27  ***********************************************************************/
28 #include "module.h"
29 #ifndef HAVE_NO_NETINET
30   #include <netinet/in.h>
31 #endif
32 
33 /************************************************************************
34  * Definitions
35  ***********************************************************************/
36 typedef union ip_s {
37   uint32_t addr;
38   uint8_t  digit[4];
39 } ip_u;
40 
41 typedef union port_s {
42   uint16_t port;
43   uint8_t  digit[2];
44 } port_u;
45 
46 /************************************************************************
47  * Globals
48  ***********************************************************************/
49 
50 /************************************************************************
51  * Local
52  ***********************************************************************/
53 /**
54  * check if string is an IPv4 address
55  * @param addr IN addr to check
56  * @return 1 if it is IPv4 else 0
57  */
socks_is_ipv4(const char * addr)58 static int socks_is_ipv4(const char *addr) {
59   return apr_isdigit(addr[0]);
60 }
61 
62 /************************************************************************
63  * Commands
64  ***********************************************************************/
65 /**
66  * Do socks proxy handshake.
67  * @param worker IN callee
68  * @param parent IN caller
69  * @param ptmp IN temp pool
70  * @return apr status
71  */
block_SOCKS_CONNECT(worker_t * worker,worker_t * parent,apr_pool_t * ptmp)72 static apr_status_t block_SOCKS_CONNECT(worker_t *worker, worker_t *parent,
73                                         apr_pool_t *ptmp) {
74   port_u port;
75   unsigned char buf[10];
76   apr_status_t status;
77   char *hostname = store_get_copy(worker->params, ptmp, "1");
78   const char *portname = store_get(worker->params, "2");
79   transport_t *transport;
80   apr_size_t len;
81 
82   if (!worker->socket) {
83     worker_log(worker, LOG_ERR, "Can not send initial SOCKS bytes");
84     return APR_ENOSOCKET;
85   }
86 
87   transport = worker->socket->transport;
88 
89   buf[0] = 5; buf[1] = 1; buf[2] = 0;
90   if ((status = transport_write(transport, (char *)buf, 3)) != APR_SUCCESS) {
91     worker_log(worker, LOG_ERR, "Can not send initial SOCKS bytes");
92     return status;
93   }
94 
95   len = 2;
96   if ((status = transport_read(transport, (char *)buf, &len)) != APR_SUCCESS) {
97     worker_log(worker, LOG_ERR, "Can not read initial SOCKS bytes");
98     return status;
99   }
100 
101   if (len != 2 || buf[0] != 5 || buf[1] != 0) {
102     worker_log(worker, LOG_ERR, "Wrong protocol bytes received");
103     return APR_EINVAL;
104   }
105 
106   buf[0] = 5; buf[1] = 1; buf[2] = 0;
107 
108   if (socks_is_ipv4(hostname)) {
109     ip_u ip;
110     char *last;
111     char *digit = apr_strtok(hostname, ".", &last);
112     int i = 0;
113     ip.addr = 0;
114     while (digit) {
115       ip.digit[i] = atoi(digit);
116       digit = apr_strtok(NULL, ".", &last);
117       i++;
118     }
119 
120     /* ATYPE IPv4 */
121     buf[3] = 1;
122     for (i = 0; i < 4; i++) {
123       buf[4 + i] = ip.digit[i];
124     }
125     if ((status = transport_write(transport, (char *)buf, 8)) != APR_SUCCESS) {
126       worker_log(worker, LOG_ERR, "Can not send IP to SOCKS proxy");
127       return status;
128     }
129   }
130   else {
131     /* ATYPE Domain name */
132     buf[3] = 3;
133     buf[4] = strlen(hostname);
134     if ((status = transport_write(transport, (char *)buf, 5)) != APR_SUCCESS) {
135       worker_log(worker, LOG_ERR, "Can not send hostname to SOCKS proxy");
136       return status;
137     }
138     if ((status = transport_write(transport, hostname, buf[4])) != APR_SUCCESS) {
139       worker_log(worker, LOG_ERR, "Can not send hostname to SOCKS proxy");
140       return status;
141     }
142   }
143 
144   port.port = atoi(portname);
145   port.port = htons(port.port);
146 
147   if ((status = transport_write(transport, (char *)port.digit, 2)) != APR_SUCCESS) {
148     worker_log(worker, LOG_ERR, "Can not send port to SOCKS proxy");
149     return status;
150   }
151 
152   len = 10;
153   if ((status = transport_read(transport, (char *)buf, &len)) != APR_SUCCESS) {
154     worker_log(worker, LOG_ERR, "Can not read final SOCKS bytes");
155     return status;
156   }
157   return APR_SUCCESS;
158 }
159 
160 /************************************************************************
161  * Module
162  ***********************************************************************/
socks_module_init(global_t * global)163 apr_status_t socks_module_init(global_t *global) {
164   apr_status_t status;
165   if ((status = module_command_new(global, "SOCKS", "_CONNECT",
166 	                           "<remote-host> <remote-port>",
167 	                           "Do run socks protocol over a established TCP connection",
168 	                           block_SOCKS_CONNECT)) != APR_SUCCESS) {
169     return status;
170   }
171 
172   return APR_SUCCESS;
173 }
174 
175 
176