Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/src/winsock.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 132 - (show annotations) (download) (as text)
Tue Sep 13 17:40:57 2016 UTC (7 years, 8 months ago) by z0rac
File MIME type: text/x-c++src
File size: 16044 byte(s)


1 /*
2 * Copyright (C) 2009-2016 TSUBAKIMOTO Hiroya <z0rac@users.sourceforge.jp>
3 *
4 * This software comes with ABSOLUTELY NO WARRANTY; for details of
5 * the license terms, see the LICENSE.txt file included with the program.
6 */
7 #include "winsock.h"
8 #include "win32.h"
9 #include <cassert>
10 #include <algorithm>
11 #include <climits>
12
13 #ifdef _DEBUG
14 #include <iostream>
15 #define DBG(s) s
16 #define LOG(s) (cout << s)
17 #else
18 #define DBG(s)
19 #define LOG(s)
20 #endif
21
22 #ifndef SEC_E_CONTEXT_EXPIRED
23 #define SEC_E_CONTEXT_EXPIRED (-2146893033)
24 #define SEC_I_CONTEXT_EXPIRED 590615
25 #endif
26
27 /*
28 * Functions of the class winsock
29 */
30 namespace {
31 int WSAAPI
32 _getaddrinfo(const char* node, const char* service,
33 const struct addrinfo* hints, struct addrinfo** res)
34 {
35 assert(hints && hints->ai_flags == 0 &&
36 hints->ai_socktype == SOCK_STREAM && hints->ai_protocol == 0);
37
38 LOG("Call _getaddrinfo()" << endl);
39
40 struct sockaddr_in sa = { AF_INET };
41 struct addrinfo ai = {
42 0, hints->ai_family != AF_UNSPEC ? hints->ai_family : AF_INET, SOCK_STREAM, IPPROTO_TCP
43 };
44 if (ai.ai_family != AF_INET) return EAI_FAMILY;
45
46 if (service) { // service to port number
47 char* end;
48 unsigned n = strtoul(service, &end, 10);
49 if (*end || n > 65535) {
50 struct servent* ent = getservbyname(service, "tcp");
51 if (!ent) return h_errno;
52 sa.sin_port = ent->s_port;
53 } else {
54 sa.sin_port = htons(static_cast<u_short>(n));
55 }
56 }
57
58 if (node && !*node) node = NULL;
59 sa.sin_addr.s_addr = inet_addr(node);
60 if (sa.sin_addr.s_addr == INADDR_NONE) {
61 struct hostent* ent = gethostbyname(node);
62 if (!ent) return h_errno;
63 if (ent->h_addrtype != AF_INET ||
64 ent->h_length != sizeof(sa.sin_addr)) return EAI_FAMILY;
65 char** hal = ent->h_addr_list;
66 int n = 0;
67 while (hal && hal[n]) ++n;
68 if (n == 0) return EAI_NONAME;
69
70 *res = reinterpret_cast<struct addrinfo*>(new char[(sizeof(ai) + sizeof(sa)) * n]);
71 struct addrinfo* ail = *res;
72 struct sockaddr_in* sal = reinterpret_cast<struct sockaddr_in*>(*res + n);
73 for (int i = 0; i < n; ++i) {
74 sa.sin_addr = *reinterpret_cast<struct in_addr*>(hal[i]);
75 ai.ai_addr = reinterpret_cast<struct sockaddr*>(&sal[i]);
76 ai.ai_next = i + 1 < n ? &ail[i + 1] : NULL;
77 ail[i] = ai, sal[i] = sa;
78 }
79 } else {
80 *res = reinterpret_cast<struct addrinfo*>(new char[sizeof(ai) + sizeof(sa)]);
81 ai.ai_addr = reinterpret_cast<struct sockaddr*>(*res + 1);
82 **res = ai;
83 *reinterpret_cast<struct sockaddr_in*>(ai.ai_addr) = sa;
84 }
85 return 0;
86 }
87
88 void WSAAPI
89 _freeaddrinfo(struct addrinfo* info)
90 {
91 LOG("Call _freeaddrinfo()" << endl);
92 delete [] reinterpret_cast<char*>(info);
93 }
94 }
95
96 winsock::_get_t winsock::_get = NULL;
97 winsock::_free_t winsock::_free = NULL;
98
99 winsock::winsock()
100 {
101 static win32::dll ws2("ws2_32.dll");
102 _get = _get_t(ws2("getaddrinfo", FARPROC(_getaddrinfo)));
103 _free = _free_t(ws2("freeaddrinfo", FARPROC(_freeaddrinfo)));
104
105 WSADATA wsa;
106 if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) throw error();
107 LOG("Winsock version: " <<
108 int(HIBYTE(wsa.wVersion)) << '.' << int(LOBYTE(wsa.wVersion)) << " <= " <<
109 int(HIBYTE(wsa.wHighVersion)) << '.' << int(LOBYTE(wsa.wHighVersion)) << endl);
110 }
111
112 struct addrinfo*
113 winsock::getaddrinfo(const string& host, const string& port, int domain)
114 {
115 struct addrinfo hints = { 0, domain, SOCK_STREAM };
116 struct addrinfo* res;
117 int err = _get(idn(host).c_str(), port.c_str(), &hints, &res);
118 if (err == 0) return res;
119 WSASetLastError(err);
120 throw error();
121 }
122
123 string
124 winsock::error::emsg()
125 {
126 char s[35];
127 return string("winsock error #") + _ltoa(WSAGetLastError(), s, 10);
128 }
129
130 /*
131 * Functions of the class winsock::tcpclient
132 */
133 winsock::tcpclient&
134 winsock::tcpclient::connect(const string& host, const string& port, int domain)
135 {
136 shutdown();
137 LOG("Connect: " << host << "(" << idn(host) << "):" << port << endl);
138 struct addrinfo* ai = getaddrinfo(host, port, domain);
139 for (struct addrinfo* p = ai; p; p = p->ai_next) {
140 SOCKET s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
141 if (s == INVALID_SOCKET) continue;
142 if (::connect(s, p->ai_addr, int(p->ai_addrlen)) == 0) {
143 _socket = s;
144 break;
145 }
146 closesocket(s);
147 }
148 winsock::freeaddrinfo(ai);
149 if (_socket == INVALID_SOCKET) throw error();
150 return *this;
151 }
152
153 winsock::tcpclient&
154 winsock::tcpclient::shutdown()
155 {
156 if (_socket != INVALID_SOCKET) {
157 ::shutdown(_socket, SD_BOTH);
158 char buf[32];
159 while (::recv(_socket, buf, sizeof(buf), 0) > 0) continue;
160 closesocket(_socket);
161 _socket = INVALID_SOCKET;
162 }
163 return *this;
164 }
165
166 size_t
167 winsock::tcpclient::recv(char* buf, size_t size)
168 {
169 int n = ::recv(_socket, buf, int(min<size_t>(size, INT_MAX)), 0);
170 if (n >= 0) return n;
171 if (WSAGetLastError() != WSAEWOULDBLOCK) throw error();
172 return 0;
173 }
174
175 size_t
176 winsock::tcpclient::send(const char* data, size_t size)
177 {
178 int n = ::send(_socket, data, int(min<size_t>(size, INT_MAX)), 0);
179 if (n >= 0) return n;
180 if (WSAGetLastError() != WSAEWOULDBLOCK) throw error();
181 return 0;
182 }
183
184 winsock::tcpclient&
185 winsock::tcpclient::timeout(int sec)
186 {
187 assert(_socket != INVALID_SOCKET);
188 u_long nb = u_long(sec < 0);
189 if (ioctlsocket(_socket, FIONBIO, &nb) != 0) throw error();
190 if (sec >= 0) {
191 int ms = sec * 1000;
192 if (setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&ms, sizeof(ms)) != 0 ||
193 setsockopt(_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&ms, sizeof(ms)) != 0) {
194 throw error();
195 }
196 }
197 return *this;
198 }
199
200 bool
201 winsock::tcpclient::wait(int op, int sec)
202 {
203 assert(_socket != INVALID_SOCKET);
204 fd_set fds;
205 FD_ZERO(&fds);
206 FD_SET(_socket, &fds);
207 fd_set* fdsp[2] = { NULL, NULL };
208 fdsp[op != 0] = &fds;
209 timeval tv = { sec, 0 };
210 int n = select(0, fdsp[0], fdsp[1], NULL, sec < 0 ? NULL : &tv);
211 if (n == 0 && sec) {
212 WSASetLastError(WSAETIMEDOUT);
213 n = -1;
214 }
215 if (n < 0) throw error();
216 return n > 0;
217 }
218
219 /*
220 * Functions of the class winsock::tlsclient
221 */
222 winsock::tlsclient::tlsclient(DWORD proto)
223 : _avail(false)
224 {
225 SCHANNEL_CRED auth = { SCHANNEL_CRED_VERSION };
226 auth.grbitEnabledProtocols = proto;
227 auth.dwFlags = (SCH_CRED_USE_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION);
228 _ok(AcquireCredentialsHandle(NULL, const_cast<SEC_CHAR*>(UNISP_NAME_A),
229 SECPKG_CRED_OUTBOUND, NULL, &auth, NULL, NULL, &_cred, NULL));
230 }
231
232 winsock::tlsclient::~tlsclient()
233 {
234 assert(!_avail);
235 FreeCredentialsHandle(&_cred);
236 }
237
238 string
239 winsock::tlsclient::_emsg(SECURITY_STATUS ss)
240 {
241 char s[9];
242 return string("SSPI error #0x") + _ultoa(ss, s, 16);
243 }
244
245 SECURITY_STATUS
246 winsock::tlsclient::_ok(SECURITY_STATUS ss) const
247 {
248 if (FAILED(ss)) throw error(_emsg(ss));
249 return ss;
250 }
251
252 SECURITY_STATUS
253 winsock::tlsclient::_token(SecBufferDesc* inb)
254 {
255 const ULONG req = (ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
256 ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR |
257 ISC_REQ_STREAM | ISC_REQ_ALLOCATE_MEMORY |
258 ISC_REQ_USE_SUPPLIED_CREDS); // for Win2kPro
259 ULONG attr;
260 struct obuf : public SecBuffer {
261 obuf() { cbBuffer = 0, BufferType = SECBUFFER_TOKEN, pvBuffer = 0; }
262 ~obuf() { if (pvBuffer) FreeContextBuffer(pvBuffer); }
263 } out;
264 SecBufferDesc outb = { SECBUFFER_VERSION, 1, &out };
265 SECURITY_STATUS ss =
266 InitializeSecurityContext(&_cred, _avail ? &_ctx : NULL,
267 NULL, req, 0, 0, inb, 0, &_ctx, &outb, &attr, NULL);
268 _avail = true;
269 switch (ss) {
270 case SEC_I_COMPLETE_AND_CONTINUE: ss = SEC_I_CONTINUE_NEEDED;
271 case SEC_I_COMPLETE_NEEDED: _ok(CompleteAuthToken(&_ctx, &outb));
272 }
273 _sendtoken((const char*)out.pvBuffer, out.cbBuffer);
274 return ss;
275 }
276
277 void
278 winsock::tlsclient::_sendtoken(const char* data, size_t size)
279 {
280 while (size) {
281 size_t n = _send(data, size);
282 data += n, size -= n;
283 }
284 }
285
286 size_t
287 winsock::tlsclient::_copyextra(size_t i, size_t size)
288 {
289 size_t n = min<size_t>(_extra.size(), size - i);
290 CopyMemory(_buf.data + i, _extra.data(), n);
291 _extra.erase(0, n);
292 return n;
293 }
294
295 winsock::tlsclient&
296 winsock::tlsclient::connect()
297 {
298 try {
299 SECURITY_STATUS ss = _extra.empty() ? _token() : SEC_I_CONTINUE_NEEDED;
300 const size_t buflen = 16 * 1024;
301 _buf(buflen);
302 size_t n = 0;
303 while (ss == SEC_I_CONTINUE_NEEDED) {
304 size_t t = !_extra.empty() ?
305 _copyextra(n, buflen) :
306 _recv(_buf.data + n, buflen - n);
307 if (t == 0) throw error(_emsg(SEC_E_INCOMPLETE_MESSAGE));
308 n += t;
309 SecBuffer in[2] = { { DWORD(n), SECBUFFER_TOKEN, _buf.data } };
310 SecBufferDesc inb = { SECBUFFER_VERSION, 2, in };
311 ss = _token(&inb);
312 if (ss == SEC_E_INCOMPLETE_MESSAGE) {
313 if (n < buflen) ss = SEC_I_CONTINUE_NEEDED;
314 } else {
315 n = 0;
316 if (in[1].BufferType == SECBUFFER_EXTRA) {
317 n = in[1].cbBuffer;
318 memmove(_buf.data, _buf.data + in[0].cbBuffer - n, n);
319 }
320 }
321 _ok(ss);
322 }
323 _extra.assign(_buf.data, n);
324 _ok(QueryContextAttributes(&_ctx, SECPKG_ATTR_STREAM_SIZES, &_sizes));
325 _buf(_sizes.cbHeader + _sizes.cbMaximumMessage + _sizes.cbTrailer);
326 } catch (...) {
327 shutdown();
328 throw;
329 }
330 return *this;
331 }
332
333 winsock::tlsclient&
334 winsock::tlsclient::shutdown()
335 {
336 if (_avail) {
337 _recvq.clear(), _extra.clear();
338 try {
339 #if __MINGW32__ && defined(ApplyControlToken) // for MinGWs bug.
340 win32::dll secur32("secur32.dll");
341 typedef SECURITY_STATUS (WINAPI* act)(PCtxtHandle, PSecBufferDesc);
342 act ApplyControlTokenA = act(secur32("ApplyControlToken"));
343 #endif
344 DWORD value = SCHANNEL_SHUTDOWN;
345 SecBuffer in = { sizeof(value), SECBUFFER_TOKEN, &value };
346 SecBufferDesc inb = { SECBUFFER_VERSION, 1, &in };
347 _ok(ApplyControlToken(&_ctx, &inb));
348 while (_token() == SEC_I_CONTINUE_NEEDED) continue;
349 } catch (...) {
350 LOG("SSPI: shutdown error." << endl);
351 }
352 DeleteSecurityContext(&_ctx);
353 _avail = false;
354 }
355 return *this;
356 }
357
358 bool
359 winsock::tlsclient::verify(const string& cn, DWORD ignore)
360 {
361 LOG("Auth: " << cn << "(" << idn(cn) << ")... ");
362 win32::wstr name(idn(cn));
363 PCCERT_CHAIN_CONTEXT chain;
364 {
365 PCCERT_CONTEXT context;
366 _ok(QueryContextAttributes(&_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &context));
367 CERT_CHAIN_PARA ccp = { sizeof(CERT_CHAIN_PARA) };
368 BOOL ok = CertGetCertificateChain(NULL, context, NULL,
369 context->hCertStore, &ccp, 0, NULL, &chain);
370 CertFreeCertificateContext(context);
371 win32::valid(ok);
372 }
373 CERT_CHAIN_POLICY_STATUS status = { sizeof(CERT_CHAIN_POLICY_STATUS) };
374 {
375 CERT_CHAIN_POLICY_PARA policy = { sizeof(CERT_CHAIN_POLICY_PARA) };
376 SSL_EXTRA_CERT_CHAIN_POLICY_PARA ssl;
377 ZeroMemory(&ssl, sizeof(ssl));
378 ssl.cbStruct = sizeof(ssl);
379 ssl.dwAuthType = AUTHTYPE_SERVER;
380 ssl.fdwChecks = ignore;
381 ssl.pwszServerName = const_cast<LPWSTR>(LPCWSTR(name));
382 policy.pvExtraPolicyPara = &ssl;
383 BOOL ok = CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
384 chain, &policy, &status);
385 CertFreeCertificateChain(chain);
386 win32::valid(ok);
387 }
388 LOG(status.dwError << endl);
389 return !status.dwError;
390 }
391
392 size_t
393 winsock::tlsclient::recv(char* buf, size_t size)
394 {
395 assert(_avail);
396 if (!_recvq.empty()) {
397 size_t n = min<size_t>(size, _recvq.size() - _rest);
398 CopyMemory(buf, _recvq.data() + _rest, n);
399 _rest += n;
400 if (_rest == _recvq.size()) _recvq.clear();
401 return n;
402 }
403 _rest = 0;
404 size_t done = 0;
405 size_t n = 0;
406 while (size && !done) {
407 size_t t = !_extra.empty() ?
408 _copyextra(n, _sizes.cbMaximumMessage) :
409 _recv(_buf.data + n, _sizes.cbMaximumMessage - n);
410 if (t == 0) {
411 if (n == 0) break;
412 throw error(_emsg(SEC_E_INCOMPLETE_MESSAGE));
413 }
414 n += t;
415 SecBuffer dec[4] = { { DWORD(n), SECBUFFER_DATA, _buf.data } };
416 SecBufferDesc decb = { SECBUFFER_VERSION, 4, dec };
417 SECURITY_STATUS ss = DecryptMessage(&_ctx, &decb, 0, NULL);
418 if (ss == SEC_E_INCOMPLETE_MESSAGE && n < _sizes.cbMaximumMessage) continue;
419 _ok(ss), n = 0;
420 string extra;
421 for (int i = 0; i < 4; ++i) {
422 switch(dec[i].BufferType) {
423 case SECBUFFER_DATA:
424 if (dec[i].cbBuffer) {
425 size_t m = 0;
426 if (done < size) {
427 m = min<size_t>(size - done, dec[i].cbBuffer);
428 CopyMemory(buf + done, dec[i].pvBuffer, m);
429 done += m;
430 }
431 _recvq.append((char*)dec[i].pvBuffer + m, dec[i].cbBuffer - m);
432 } else if (ss == SEC_E_OK && _buf.data[0] == 0x15) {
433 ss = SEC_I_CONTEXT_EXPIRED; // for Win2kPro
434 }
435 break;
436 case SECBUFFER_EXTRA:
437 extra.append((char*)dec[i].pvBuffer, dec[i].cbBuffer);
438 break;
439 }
440 }
441 _extra = extra + _extra;
442 if (ss == SEC_I_CONTEXT_EXPIRED && !done) {
443 throw error(_emsg(SEC_E_CONTEXT_EXPIRED));
444 }
445 if (ss == SEC_I_RENEGOTIATE) connect();
446 }
447 return done;
448 }
449
450 size_t
451 winsock::tlsclient::send(const char* data, size_t size)
452 {
453 assert(_avail);
454 if (size) {
455 size = min<size_t>(size, _sizes.cbMaximumMessage);
456 SecBuffer enc[4] = {
457 { _sizes.cbHeader, SECBUFFER_STREAM_HEADER, _buf.data },
458 { DWORD(size), SECBUFFER_DATA, _buf.data + _sizes.cbHeader },
459 { _sizes.cbTrailer, SECBUFFER_STREAM_TRAILER,
460 _buf.data + _sizes.cbHeader + size }
461 };
462 SecBufferDesc encb = { SECBUFFER_VERSION, 4, enc };
463 CopyMemory(_buf.data + _sizes.cbHeader, data, size);
464 _ok(EncryptMessage(&_ctx, 0, &encb, 0));
465 _sendtoken(_buf.data, enc[0].cbBuffer + enc[1].cbBuffer + enc[2].cbBuffer);
466 }
467 return size;
468 }
469
470 /*
471 * Functions of IDN
472 */
473 string
474 winsock::idn(const string& host)
475 {
476 string::size_type i = 0;
477 while (i < host.size() && BYTE(host[i]) < 0x80) ++i;
478 return i == host.size() ? host : idn(win32::wstr(host));
479 }
480
481 string
482 winsock::idn(LPCWSTR host)
483 {
484 struct lambda {
485 static string encode(LPCWSTR input, size_t length, size_t n) {
486 static const error overflow("punycode overflow");
487 string output;
488 size_t delta = 0, bias = 72, damp = 700;
489 for (WCHAR code = 0x80; n < length; ++code) {
490 WCHAR next = WCHAR(-1);
491 for (size_t i = 0; i < length; ++i) {
492 if (input[i] >= code && input[i] < next) next = input[i];
493 }
494 size_t t = delta + (next - code) * (n + 1);
495 if (t < delta) throw overflow;
496 delta = t, code = next;
497 for (size_t i = 0; i < length; ++i) {
498 if (input[i] != code) {
499 if (input[i] < code && ++delta == 0) throw overflow;
500 } else {
501 enum { base = 36, tmin = 1, tmax = 26, skew = 38 };
502 static const char b36[] = "abcdefghijklmnopqrstuvwxyz0123456789";
503 size_t q = delta;
504 for (size_t k = base;; k += base) {
505 size_t qd = k > bias ? min<size_t>(k - bias, tmax) : tmin;
506 if (q < qd) break;
507 output += b36[qd + (q - qd) % (base - qd)];
508 q = (q - qd) / (base - qd);
509 }
510 output += b36[q];
511 size_t k = 0;
512 q = delta / damp, q += q / ++n;
513 for (; q > (base - tmin) * tmax / 2; q /= base - tmin) k += base;
514 bias = k + (base - tmin + 1) * q / (q + skew);
515 delta = 0, damp = 2;
516 }
517 }
518 if (++delta == 0) throw overflow;
519 }
520 return output;
521 }
522 };
523
524 string result;
525 for (LPCWSTR p = host; *p; ++p) {
526 string asc;
527 LPCWSTR t = p;
528 for (; *p && *p != '.'; ++p) {
529 if (*p < 0x80) asc += static_cast<string::value_type>(*p);
530 }
531 if (asc.size() < static_cast<string::size_type>(p - t)) {
532 result += "xn--";
533 if (!asc.empty()) result += asc, result += '-';
534 result += lambda::encode(t, p - t, asc.size());
535 } else result += asc;
536 if (!*p) break;
537 result += static_cast<string::value_type>(*p);
538 }
539 return result;
540 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26