pn_socket_t pn_listen(pn_io_t *io, const char *host, const char *port) { struct addrinfo *addr; int code = getaddrinfo(host, amqp_service(port), NULL, &addr); if (code) { pn_error_format(io->error, PN_ERR, "getaddrinfo(%s, %s): %s\n", host, port, gai_strerror(code)); return INVALID_SOCKET; } pn_socket_t sock = pni_create_socket(addr->ai_family); if (sock == INVALID_SOCKET) { pni_win32_error(io->error, "pni_create_socket", WSAGetLastError()); return INVALID_SOCKET; } ensure_unique(io, sock); bool optval = 1; if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char *) &optval, sizeof(optval)) == -1) { pni_win32_error(io->error, "setsockopt", WSAGetLastError()); closesocket(sock); return INVALID_SOCKET; } if (bind(sock, addr->ai_addr, addr->ai_addrlen) == -1) { pni_win32_error(io->error, "bind", WSAGetLastError()); freeaddrinfo(addr); closesocket(sock); return INVALID_SOCKET; } freeaddrinfo(addr); if (listen(sock, 50) == -1) { pni_win32_error(io->error, "listen", WSAGetLastError()); closesocket(sock); return INVALID_SOCKET; } if (io->iocp->selector) { iocpdesc_t *iocpd = pni_iocpdesc_create(io->iocp, sock, false); if (!iocpd) { pn_i_error_from_errno(io->error, "register"); closesocket(sock); return INVALID_SOCKET; } pni_iocpdesc_start(iocpd); } return sock; }
pn_socket_t pni_iocp_end_accept(iocpdesc_t *ld, sockaddr *addr, socklen_t *addrlen, bool *would_block, pn_error_t *error) { if (!is_listener(ld)) { set_iocp_error_status(error, PN_ERR, WSAEOPNOTSUPP); return INVALID_SOCKET; } if (ld->read_closed) { set_iocp_error_status(error, PN_ERR, WSAENOTSOCK); return INVALID_SOCKET; } if (pn_list_size(ld->acceptor->accepts) == 0) { if (ld->events & PN_READABLE && ld->iocp->iocp_trace) iocp_log("listen socket readable with no available accept completions\n"); *would_block = true; return INVALID_SOCKET; } accept_result_t *result = (accept_result_t *) pn_list_get(ld->acceptor->accepts, 0); pn_list_del(ld->acceptor->accepts, 0, 1); if (!pn_list_size(ld->acceptor->accepts)) pni_events_update(ld, ld->events & ~PN_READABLE); // No pending accepts pn_socket_t accept_sock; if (result->base.status) { accept_sock = INVALID_SOCKET; pni_win32_error(ld->error, "accept failure", result->base.status); if (ld->iocp->iocp_trace) iocp_log("%s\n", pn_error_text(ld->error)); // App never sees this socket so close it here. pni_iocp_begin_close(result->new_sock); } else { accept_sock = result->new_sock->socket; // AcceptEx special setsockopt: setsockopt(accept_sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&ld->socket, sizeof (SOCKET)); if (addr && addrlen && *addrlen > 0) { sockaddr_storage *local_addr = NULL; sockaddr_storage *remote_addr = NULL; int local_addrlen, remote_addrlen; LPFN_GETACCEPTEXSOCKADDRS fn = ld->acceptor->fn_get_accept_ex_sockaddrs; fn(result->address_buffer, 0, IOCP_SOCKADDRMAXLEN, IOCP_SOCKADDRMAXLEN, (SOCKADDR **) &local_addr, &local_addrlen, (SOCKADDR **) &remote_addr, &remote_addrlen); *addrlen = pn_min(*addrlen, remote_addrlen); memmove(addr, remote_addr, *addrlen); } } if (accept_sock != INVALID_SOCKET) { // Connected. result->new_sock->read_closed = false; result->new_sock->write_closed = false; } // Done with the completion result, so reuse it result->new_sock = NULL; begin_accept(ld->acceptor, result); return accept_sock; }
int pn_pipe(pn_io_t *io, pn_socket_t *dest) { int n = pni_socket_pair(io, dest); if (n) { pni_win32_error(io->error, "pipe", WSAGetLastError()); } return n; }
static void iocpdesc_fail(iocpdesc_t *iocpd, HRESULT status, const char* text) { pni_win32_error(iocpd->error, text, status); if (iocpd->iocp->iocp_trace) { iocp_log("connection terminated: %s\n", pn_error_text(iocpd->error)); } iocpd->write_closed = true; iocpd->read_closed = true; iocpd->poll_error = true; pni_events_update(iocpd, iocpd->events & ~(PN_READABLE | PN_WRITABLE)); }
pn_socket_t pn_connect(pn_io_t *io, const char *hostarg, const char *port) { // convert "0.0.0.0" to "127.0.0.1" on Windows for outgoing sockets const char *host = strcmp("0.0.0.0", hostarg) ? hostarg : "127.0.0.1"; struct addrinfo *addr; int code = getaddrinfo(host, amqp_service(port), NULL, &addr); if (code) { pn_error_format(io->error, PN_ERR, "getaddrinfo(%s, %s): %s", host, port, gai_strerror(code)); return INVALID_SOCKET; } pn_socket_t sock = pni_create_socket(addr->ai_family); if (sock == INVALID_SOCKET) { pni_win32_error(io->error, "proton pni_create_socket", WSAGetLastError()); freeaddrinfo(addr); return INVALID_SOCKET; } ensure_unique(io, sock); pn_configure_sock(io, sock); if (io->iocp->selector) { return pni_iocp_begin_connect(io->iocp, sock, addr, io->error); } else { if (connect(sock, addr->ai_addr, addr->ai_addrlen) != 0) { if (WSAGetLastError() != WSAEWOULDBLOCK) { pni_win32_error(io->error, "connect", WSAGetLastError()); freeaddrinfo(addr); closesocket(sock); return INVALID_SOCKET; } } freeaddrinfo(addr); return sock; } }
pn_socket_t pni_iocp_begin_connect(iocp_t *iocp, pn_socket_t sock, struct addrinfo *addr, pn_error_t *error) { // addr lives for the duration of the async connect. Caller has passed ownership here. // See connect_result_finalize(). // Use of Windows-specific ConnectEx() requires our socket to be "loosely" pre-bound: sockaddr_storage sa; memset(&sa, 0, sizeof(sa)); sa.ss_family = addr->ai_family; if (bind(sock, (SOCKADDR *) &sa, addr->ai_addrlen)) { pni_win32_error(error, "begin async connection", WSAGetLastError()); if (iocp->iocp_trace) iocp_log("%s\n", pn_error_text(error)); closesocket(sock); freeaddrinfo(addr); return INVALID_SOCKET; } iocpdesc_t *iocpd = pni_iocpdesc_create(iocp, sock, false); bind_to_completion_port(iocpd); LPFN_CONNECTEX fn_connect_ex = lookup_connect_ex(iocpd->socket); connect_result_t *result = connect_result(iocpd, addr); DWORD unused; bool success = fn_connect_ex(iocpd->socket, result->addrinfo->ai_addr, result->addrinfo->ai_addrlen, NULL, 0, &unused, (LPOVERLAPPED) result); if (!success && WSAGetLastError() != ERROR_IO_PENDING) { pni_win32_error(error, "ConnectEx failure", WSAGetLastError()); pn_free(result); iocpd->write_closed = true; iocpd->read_closed = true; pni_iocp_begin_close(iocpd); sock = INVALID_SOCKET; if (iocp->iocp_trace) iocp_log("%s\n", pn_error_text(error)); } else { iocpd->ops_in_progress++; } return sock; }
void pn_io_initialize(void *obj) { pn_io_t *io = (pn_io_t *) obj; io->error = pn_error(); io->wouldblock = false; io->trace = pn_env_bool("PN_TRACE_DRV"); /* Request WinSock 2.2 */ WORD wsa_ver = MAKEWORD(2, 2); WSADATA unused; int err = WSAStartup(wsa_ver, &unused); if (err) { pni_win32_error(io->error, "WSAStartup", WSAGetLastError()); fprintf(stderr, "Can't load WinSock: %s\n", pn_error_text(io->error)); } io->iocp = pni_iocp(); }
// returns: -1 on error, 0 on timeout, 1 successful completion int pni_iocp_wait_one(iocp_t *iocp, int timeout, pn_error_t *error) { DWORD win_timeout = (timeout < 0) ? INFINITE : (DWORD) timeout; DWORD num_transferred = 0; ULONG_PTR completion_key = 0; OVERLAPPED *overlapped = 0; bool good_op = GetQueuedCompletionStatus (iocp->completion_port, &num_transferred, &completion_key, &overlapped, win_timeout); if (!overlapped) if (GetLastError() == WAIT_TIMEOUT) return 0; else { if (error) pni_win32_error(error, "GetQueuedCompletionStatus", GetLastError()); return -1; } iocp_result_t *result = (iocp_result_t *) overlapped; complete(result, good_op, num_transferred); return 1; }
pn_socket_t pn_accept(pn_io_t *io, pn_socket_t listen_sock, char *name, size_t size) { struct sockaddr_in addr = {0}; addr.sin_family = AF_INET; socklen_t addrlen = sizeof(addr); iocpdesc_t *listend = pni_iocpdesc_map_get(io->iocp, listen_sock); pn_socket_t accept_sock; if (listend) accept_sock = pni_iocp_end_accept(listend, (struct sockaddr *) &addr, &addrlen, &io->wouldblock, io->error); else { // User supplied socket accept_sock = accept(listen_sock, (struct sockaddr *) &addr, &addrlen); if (accept_sock == INVALID_SOCKET) pni_win32_error(io->error, "sync accept", WSAGetLastError()); } if (accept_sock == INVALID_SOCKET) return accept_sock; int code = getnameinfo((struct sockaddr *) &addr, addrlen, io->host, NI_MAXHOST, io->serv, NI_MAXSERV, 0); if (code) code = getnameinfo((struct sockaddr *) &addr, addrlen, io->host, NI_MAXHOST, io->serv, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); if (code) { pn_error_format(io->error, PN_ERR, "getnameinfo: %s\n", gai_strerror(code)); pn_close(io, accept_sock); return INVALID_SOCKET; } else { pn_configure_sock(io, accept_sock); snprintf(name, size, "%s:%s", io->host, io->serv); if (listend) { pni_iocpdesc_start(pni_iocpdesc_map_get(io->iocp, accept_sock)); } return accept_sock; } }
/* * Note: iocp write completion is not "bytes on the wire", it is "peer * acked the sent bytes". Completion can be seconds on a slow * consuming peer. */ static void complete_write(write_result_t *result, DWORD xfer_count, HRESULT status) { iocpdesc_t *iocpd = result->base.iocpd; if (iocpd->closing) { pni_write_pipeline_return(iocpd->pipeline, result); if (!iocpd->write_closed && !write_in_progress(iocpd)) iocp_shutdown(iocpd); reap_check(iocpd); return; } if (status == 0 && xfer_count > 0) { if (xfer_count != result->requested) { // Is this recoverable? How to preserve order if multiple overlapped writes? pni_write_pipeline_return(iocpd->pipeline, result); iocpdesc_fail(iocpd, WSA_OPERATION_ABORTED, "Partial overlapped write on socket"); return; } else { // Success. pni_write_pipeline_return(iocpd->pipeline, result); if (pni_write_pipeline_writable(iocpd->pipeline)) pni_events_update(iocpd, iocpd->events | PN_WRITABLE); return; } } // Other error pni_write_pipeline_return(iocpd->pipeline, result); if (status == WSAECONNABORTED || status == WSAECONNRESET || status == WSAENOTCONN || status == ERROR_NETNAME_DELETED) { iocpd->write_closed = true; iocpd->poll_error = true; pni_events_update(iocpd, iocpd->events & ~PN_WRITABLE); pni_win32_error(iocpd->error, "Remote close or timeout", status); } else { iocpdesc_fail(iocpd, status, "IOCP async write error"); } }