| 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 |
} |