blob: 74f894d65bd15ce92883bf7f6cec54d97c1b2aed [file] [log] [blame]
Andy Green4739e5c2011-01-22 12:51:57 +00001#include "private-libwebsockets.h"
2#include <netdb.h>
3
4
Andy Green90c7cbc2011-01-27 06:26:52 +00005/**
6 * libwebsocket_client_connect() - Connect to another websocket server
Peter Hinz56885f32011-03-02 22:03:47 +00007 * @context: Websocket context
Andy Green90c7cbc2011-01-27 06:26:52 +00008 * @address: Remote server address, eg, "myserver.com"
9 * @port: Port to connect to on the remote server, eg, 80
10 * @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
11 * signed certs
12 * @path: Websocket path on server
13 * @host: Hostname on server
14 * @origin: Socket origin name
15 * @protocol: Comma-separated list of protocols being asked for from
16 * the server, or just one. The server will pick the one it
17 * likes best.
Andy Greenbfb051f2011-02-09 08:49:14 +000018 * @ietf_version_or_minus_one: -1 to ask to connect using the default, latest
19 * protocol supported, or the specific protocol ordinal
Andy Green90c7cbc2011-01-27 06:26:52 +000020 *
21 * This function creates a connection to a remote server
22 */
23
Andy Green4739e5c2011-01-22 12:51:57 +000024struct libwebsocket *
Peter Hinz56885f32011-03-02 22:03:47 +000025libwebsocket_client_connect(struct libwebsocket_context *context,
Andy Green4739e5c2011-01-22 12:51:57 +000026 const char *address,
27 int port,
Andy Green90c7cbc2011-01-27 06:26:52 +000028 int ssl_connection,
Andy Green4739e5c2011-01-22 12:51:57 +000029 const char *path,
30 const char *host,
31 const char *origin,
Andy Greenbfb051f2011-02-09 08:49:14 +000032 const char *protocol,
33 int ietf_version_or_minus_one)
Andy Green4739e5c2011-01-22 12:51:57 +000034{
35 struct hostent *server_hostent;
36 struct sockaddr_in server_addr;
Andy Greenbe93fef2011-02-14 20:25:43 +000037 char pkt[512];
Andy Green4739e5c2011-01-22 12:51:57 +000038 struct pollfd pfd;
Andy Green4739e5c2011-01-22 12:51:57 +000039 struct libwebsocket *wsi;
40 int n;
Andy Green9659f372011-01-27 22:01:43 +000041 int plen = 0;
Andy Greenbe93fef2011-02-14 20:25:43 +000042#ifndef LWS_OPENSSL_SUPPORT
Andy Green90c7cbc2011-01-27 06:26:52 +000043 if (ssl_connection) {
44 fprintf(stderr, "libwebsockets not configured for ssl\n");
45 return NULL;
46 }
47#endif
Andy Green4739e5c2011-01-22 12:51:57 +000048
Andy Green6964bb52011-01-23 16:50:33 +000049 wsi = malloc(sizeof(struct libwebsocket));
Andy Greenbe93fef2011-02-14 20:25:43 +000050 if (wsi == NULL)
51 goto bail1;
Andy Green4739e5c2011-01-22 12:51:57 +000052
Darin Willitsc19456f2011-02-14 17:52:39 +000053 memset(wsi, 0, sizeof *wsi);
54
Andy Greenbfb051f2011-02-09 08:49:14 +000055 /* -1 means just use latest supported */
56
57 if (ietf_version_or_minus_one == -1)
Andy Green193306c2011-02-26 11:08:46 +000058 ietf_version_or_minus_one = SPEC_LATEST_SUPPORTED;
Andy Greenbfb051f2011-02-09 08:49:14 +000059
60 wsi->ietf_spec_revision = ietf_version_or_minus_one;
Andy Green4739e5c2011-01-22 12:51:57 +000061 wsi->name_buffer_pos = 0;
62 wsi->user_space = NULL;
63 wsi->state = WSI_STATE_CLIENT_UNCONNECTED;
64 wsi->pings_vs_pongs = 0;
Andy Green927eb7b2011-02-01 08:52:55 +000065 wsi->protocol = NULL;
Andy Greena71eafc2011-02-14 17:59:43 +000066 wsi->pending_timeout = NO_PENDING_TIMEOUT;
Andy Greend6e09112011-03-05 16:12:15 +000067 wsi->count_active_extensions = 0;
Andy Greenbe93fef2011-02-14 20:25:43 +000068#ifdef LWS_OPENSSL_SUPPORT
69 wsi->use_ssl = ssl_connection;
70#endif
71
72 /* copy parameters over so state machine has access */
73
74 wsi->c_path = malloc(strlen(path) + 1);
75 if (wsi->c_path == NULL)
76 goto bail1;
77 strcpy(wsi->c_path, path);
78 wsi->c_host = malloc(strlen(host) + 1);
79 if (wsi->c_host == NULL)
80 goto oom1;
81 strcpy(wsi->c_host, host);
Andy Green08d33922011-02-26 10:22:49 +000082 if (origin) {
83 wsi->c_origin = malloc(strlen(origin) + 1);
84 strcpy(wsi->c_origin, origin);
85 if (wsi->c_origin == NULL)
86 goto oom2;
87 } else
88 wsi->c_origin = NULL;
Andy Greenbe93fef2011-02-14 20:25:43 +000089 if (protocol) {
90 wsi->c_protocol = malloc(strlen(protocol) + 1);
91 if (wsi->c_protocol == NULL)
92 goto oom3;
93 strcpy(wsi->c_protocol, protocol);
94 } else
95 wsi->c_protocol = NULL;
96
Andy Green4739e5c2011-01-22 12:51:57 +000097
Andy Greenbfb051f2011-02-09 08:49:14 +000098 /* set up appropriate masking */
99
100 wsi->xor_mask = xor_no_mask;
101
102 switch (wsi->ietf_spec_revision) {
Andy Greeneeaacb32011-03-01 20:44:24 +0000103 case 0:
104 break;
Andy Greenbfb051f2011-02-09 08:49:14 +0000105 case 4:
106 wsi->xor_mask = xor_mask_04;
107 break;
108 case 5:
Andy Green9514bf82011-02-26 11:13:56 +0000109 case 6:
Andy Greenbfb051f2011-02-09 08:49:14 +0000110 wsi->xor_mask = xor_mask_05;
111 break;
112 default:
113 fprintf(stderr,
114 "Client ietf version %d not supported\n",
115 wsi->ietf_spec_revision);
Andy Greenbe93fef2011-02-14 20:25:43 +0000116 goto oom4;
Andy Greenbfb051f2011-02-09 08:49:14 +0000117 }
118
119 /* force no mask if he asks for that though */
120
Peter Hinz56885f32011-03-02 22:03:47 +0000121 if (context->options & LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK)
Andy Greenbfb051f2011-02-09 08:49:14 +0000122 wsi->xor_mask = xor_no_mask;
123
Andy Green4739e5c2011-01-22 12:51:57 +0000124 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
125 wsi->utf8_token[n].token = NULL;
126 wsi->utf8_token[n].token_len = 0;
127 }
128
129 /*
Andy Green9659f372011-01-27 22:01:43 +0000130 * proxy?
131 */
132
Peter Hinz56885f32011-03-02 22:03:47 +0000133 if (context->http_proxy_port) {
Andy Green9659f372011-01-27 22:01:43 +0000134 plen = sprintf(pkt, "CONNECT %s:%u HTTP/1.0\x0d\x0a"
135 "User-agent: libwebsockets\x0d\x0a"
136/*Proxy-authorization: basic aGVsbG86d29ybGQ= */
137 "\x0d\x0a", address, port);
138
139 /* OK from now on we talk via the proxy */
140
Peter Hinz56885f32011-03-02 22:03:47 +0000141 address = context->http_proxy_address;
142 port = context->http_proxy_port;
Andy Green9659f372011-01-27 22:01:43 +0000143 }
144
145 /*
146 * prepare the actual connection (to the proxy, if any)
Andy Green4739e5c2011-01-22 12:51:57 +0000147 */
148
149 server_hostent = gethostbyname(address);
150 if (server_hostent == NULL) {
151 fprintf(stderr, "Unable to get host name from %s\n", address);
Andy Greenbe93fef2011-02-14 20:25:43 +0000152 goto oom4;
Andy Green4739e5c2011-01-22 12:51:57 +0000153 }
154
155 wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
Andy Green6964bb52011-01-23 16:50:33 +0000156
Andy Green4739e5c2011-01-22 12:51:57 +0000157 if (wsi->sock < 0) {
158 fprintf(stderr, "Unable to open socket\n");
Andy Greenbe93fef2011-02-14 20:25:43 +0000159 goto oom4;
Andy Green4739e5c2011-01-22 12:51:57 +0000160 }
161
Andy Green4739e5c2011-01-22 12:51:57 +0000162 server_addr.sin_family = AF_INET;
163 server_addr.sin_port = htons(port);
164 server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
165 bzero(&server_addr.sin_zero, 8);
166
167 if (connect(wsi->sock, (struct sockaddr *)&server_addr,
168 sizeof(struct sockaddr)) == -1) {
Andy Green90c7cbc2011-01-27 06:26:52 +0000169 fprintf(stderr, "Connect failed\n");
Andy Greenbe93fef2011-02-14 20:25:43 +0000170 goto oom4;
Andy Green6964bb52011-01-23 16:50:33 +0000171 }
Andy Green4739e5c2011-01-22 12:51:57 +0000172
Andy Greenbe93fef2011-02-14 20:25:43 +0000173 /* into fd -> wsi hashtable */
174
Peter Hinz56885f32011-03-02 22:03:47 +0000175 insert_wsi(context, wsi);
Andy Greenbe93fef2011-02-14 20:25:43 +0000176
177 /* into internal poll list */
178
Peter Hinz56885f32011-03-02 22:03:47 +0000179 context->fds[context->fds_count].fd = wsi->sock;
180 context->fds[context->fds_count].revents = 0;
181 context->fds[context->fds_count++].events = POLLIN;
Andy Greenbe93fef2011-02-14 20:25:43 +0000182
183 /* external POLL support via protocol 0 */
Peter Hinz56885f32011-03-02 22:03:47 +0000184 context->protocols[0].callback(context, wsi,
Andy Greenbe93fef2011-02-14 20:25:43 +0000185 LWS_CALLBACK_ADD_POLL_FD,
186 (void *)(long)wsi->sock, NULL, POLLIN);
187
Andy Green9659f372011-01-27 22:01:43 +0000188 /* we are connected to server, or proxy */
189
Peter Hinz56885f32011-03-02 22:03:47 +0000190 if (context->http_proxy_port) {
Andy Green9659f372011-01-27 22:01:43 +0000191
192 n = send(wsi->sock, pkt, plen, 0);
193 if (n < 0) {
Peter Hinz56885f32011-03-02 22:03:47 +0000194#ifdef WIN32
195 closesocket(wsi->sock);
196#else
Andy Green5b9a4c02011-01-28 09:39:29 +0000197 close(wsi->sock);
Peter Hinz56885f32011-03-02 22:03:47 +0000198#endif
Andy Green5b9a4c02011-01-28 09:39:29 +0000199 fprintf(stderr, "ERROR writing to proxy socket\n");
200 goto bail1;
201 }
202
Andy Greenbe93fef2011-02-14 20:25:43 +0000203 libwebsocket_set_timeout(wsi,
204 PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, 5);
Andy Green5b9a4c02011-01-28 09:39:29 +0000205
Andy Greenbe93fef2011-02-14 20:25:43 +0000206 wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;
Andy Green9659f372011-01-27 22:01:43 +0000207
Andy Greenbe93fef2011-02-14 20:25:43 +0000208 return wsi;
Andy Green9659f372011-01-27 22:01:43 +0000209 }
210
Andy Green6964bb52011-01-23 16:50:33 +0000211 /*
Andy Greenbe93fef2011-02-14 20:25:43 +0000212 * provoke service to issue the handshake directly
213 * we need to do it this way because in the proxy case, this is the
214 * next state and executed only if and when we get a good proxy
215 * response inside the state machine
Andy Green6964bb52011-01-23 16:50:33 +0000216 */
Andy Green4739e5c2011-01-22 12:51:57 +0000217
Andy Greenbe93fef2011-02-14 20:25:43 +0000218 wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
Andy Green4739e5c2011-01-22 12:51:57 +0000219 pfd.fd = wsi->sock;
Andy Greenbe93fef2011-02-14 20:25:43 +0000220 pfd.revents = POLLIN;
Peter Hinz56885f32011-03-02 22:03:47 +0000221 libwebsocket_service_fd(context, &pfd);
Andy Green4739e5c2011-01-22 12:51:57 +0000222
Andy Green4739e5c2011-01-22 12:51:57 +0000223 return wsi;
224
Andy Greenbe93fef2011-02-14 20:25:43 +0000225oom4:
226 if (wsi->c_protocol)
227 free(wsi->c_protocol);
Andy Green4739e5c2011-01-22 12:51:57 +0000228
Andy Greenbe93fef2011-02-14 20:25:43 +0000229oom3:
Andy Green08d33922011-02-26 10:22:49 +0000230 if (wsi->c_origin)
231 free(wsi->c_origin);
Andy Greenbe93fef2011-02-14 20:25:43 +0000232
233oom2:
234 free(wsi->c_host);
235
236oom1:
237 free(wsi->c_path);
238
Andy Green4739e5c2011-01-22 12:51:57 +0000239bail1:
240 free(wsi);
241
242 return NULL;
243}