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