ssize_t windows_send(int sockfd, const void *buf, size_t len, int flags) { u_long on = 1, off = 0; ssize_t outlen = -1; if (!check_dontwait(&flags)) { return wserr(send(sockfd, buf, len, flags)); } if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0) { outlen = wserr(send(sockfd, buf, len, flags)); ioctlsocket(sockfd, FIONBIO, &off); } return outlen; }
ssize_t windows_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { u_long on = 1, off = 0; ssize_t outlen = -1; if (!check_dontwait(&flags)) { return wserr(sendto(sockfd, buf, len, flags, dest_addr, addrlen)); } if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0) { outlen = wserr(sendto(sockfd, buf, len, flags, dest_addr, addrlen)); ioctlsocket(sockfd, FIONBIO, &off); } return outlen; }
ssize_t windows_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { u_long on = 1, off = 0; ssize_t outlen = -1; if (!check_dontwait(&flags)) { return wserr(recvfrom(sockfd, buf, len, flags, src_addr, addrlen)); } if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0) { outlen = wserr(recvfrom(sockfd, buf, len, flags, src_addr, addrlen)); ioctlsocket(sockfd, FIONBIO, &off); } return outlen; }
ssize_t windows_write(int fd, void *buf, size_t count) { ssize_t ret; ret = wserr(send(fd, buf, count, 0)); if (ret == -1 && errno == ENOTSOCK) { ret = write(fd, buf, count); } return ret; }
ssize_t windows_read(int fd, void *buf, size_t count) { ssize_t ret; ret = wserr(recv(fd, buf, count, 0)); if (ret == -1 && errno == ENOTSOCK) { ret = read(fd, buf, count); } return ret; }
/** * See header */ int poll(struct pollfd *fds, int nfds, int timeout) { return wserr(WSAPoll(fds, nfds, timeout)); }
/** * See header */ int socketpair(int domain, int type, int protocol, int sv[2]) { struct sockaddr_in addr = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), }; socklen_t len = sizeof(addr); int s, c, sc; BOOL on; /* We don't check domain for AF_INET, as we use it as replacement for * AF_UNIX. */ if (type != SOCK_STREAM) { errno = EINVAL; return -1; } if (protocol != 0 && protocol != IPPROTO_TCP) { errno = EINVAL; return -1; } s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) { return -1; } c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (c == -1) { closesocket(s); return -1; } if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == 0 && getsockname(s,(struct sockaddr*)&addr, &len) == 0 && listen(s, 0) == 0 && connect(c, (struct sockaddr*)&addr, sizeof(addr)) == 0) { sc = accept(s, NULL, NULL); if (sc >= 0) { closesocket(s); s = sc; if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) == 0 && setsockopt(c, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) == 0) { sv[0] = s; sv[1] = c; return 0; } } } closesocket(s); closesocket(c); return -1; } /** * See header */ char* getpass(const char *prompt) { static char buf[64] = ""; char *pos; HANDLE in, out; DWORD mode, written = 0, total, done; out = GetStdHandle(STD_OUTPUT_HANDLE); in = GetStdHandle(STD_INPUT_HANDLE); if (out == INVALID_HANDLE_VALUE || in == INVALID_HANDLE_VALUE || !GetConsoleMode(out, &mode) || !GetConsoleMode(in, &mode)) { return NULL; } total = strlen(prompt); while (written < total) { if (!WriteConsole(out, prompt + written, total - written, &done, NULL)) { return NULL; } written += done; } if (!SetConsoleMode(in, mode & ~ENABLE_ECHO_INPUT)) { return NULL; } while (TRUE) { if (!ReadConsole(in, buf, sizeof(buf), &done, NULL)) { SetConsoleMode(in, mode); return NULL; } buf[sizeof(buf)-1] = '\0'; if (done) { pos = strchr(buf, '\r'); if (pos) { *pos = '\0'; } break; } } SetConsoleMode(in, mode); /* append a newline, as we have no echo during input */ WriteConsole(out, "\r\n", 2, &done, NULL); return buf; } /** * See header. */ #undef strerror_s int strerror_s_extended(char *buf, size_t buflen, int errnum) { const char *errstr [] = { /* EADDRINUSE */ "Address in use", /* EADDRNOTAVAIL */ "Address not available", /* EAFNOSUPPORT */ "Address family not supported", /* EALREADY */ "Connection already in progress", /* EBADMSG */ "Bad message", /* ECANCELED */ "Operation canceled", /* ECONNABORTED */ "Connection aborted", /* ECONNREFUSED */ "Connection refused", /* ECONNRESET */ "Connection reset", /* EDESTADDRREQ */ "Destination address required", /* EHOSTUNREACH */ "Host is unreachable", /* EIDRM */ "Identifier removed", /* EINPROGRESS */ "Operation in progress", /* EISCONN */ "Socket is connected", /* ELOOP */ "Too many levels of symbolic links", /* EMSGSIZE */ "Message too large", /* ENETDOWN */ "Network is down", /* ENETRESET */ "Connection aborted by network", /* ENETUNREACH */ "Network unreachable", /* ENOBUFS */ "No buffer space available", /* ENODATA */ "No message is available", /* ENOLINK */ "No link", /* ENOMSG */ "No message of the desired type", /* ENOPROTOOPT */ "Protocol not available", /* ENOSR */ "No stream resources", /* ENOSTR */ "Not a stream", /* ENOTCONN */ "The socket is not connected", /* ENOTRECOVERABLE */ "State not recoverable", /* ENOTSOCK */ "Not a socket", /* ENOTSUP */ "Not supported", /* EOPNOTSUPP */ "Operation not supported on socket", /* EOTHER */ "Other error", /* EOVERFLOW */ "Value too large to be stored in data type", /* EOWNERDEAD */ "Previous owner died", /* EPROTO */ "Protocol error", /* EPROTONOSUPPORT */ "Protocol not supported", /* EPROTOTYPE */ "Protocol wrong type for socket", /* ETIME */ "Timeout", /* ETIMEDOUT */ "Connection timed out", /* ETXTBSY */ "Text file busy", /* EWOULDBLOCK */ "Operation would block", }; int offset = EADDRINUSE; if (errnum < offset || errnum >= offset + countof(errstr)) { return strerror_s(buf, buflen, errnum); } strncpy(buf, errstr[errnum - offset], buflen); buf[buflen - 1] = '\0'; return 0; } /** * Set errno for a function setting WSA error on failure */ static int wserr(int retval) { if (retval < 0) { static const struct { DWORD wsa; int err; } map[] = { { WSANOTINITIALISED, EBADF }, { WSAENETDOWN, ENETDOWN }, { WSAENETRESET, ENETRESET }, { WSAECONNABORTED, ECONNABORTED }, { WSAESHUTDOWN, ECONNABORTED }, { WSAEACCES, EACCES }, { WSAEINTR, EINTR }, { WSAEINPROGRESS, EINPROGRESS }, { WSAEFAULT, EFAULT }, { WSAENOBUFS, ENOBUFS }, { WSAENOTSOCK, ENOTSOCK }, { WSAEOPNOTSUPP, EOPNOTSUPP }, { WSAEWOULDBLOCK, EWOULDBLOCK }, { WSAEMSGSIZE, EMSGSIZE }, { WSAEINVAL, EINVAL }, { WSAENOTCONN, ENOTCONN }, { WSAEHOSTUNREACH, EHOSTUNREACH }, { WSAENETUNREACH, ENETUNREACH }, { WSAECONNABORTED, ECONNABORTED }, { WSAECONNRESET, ECONNRESET }, { WSAETIMEDOUT, ETIMEDOUT }, { WSAEMFILE, EMFILE }, { WSAEALREADY, EALREADY }, { WSAEDESTADDRREQ, EDESTADDRREQ }, { WSAEISCONN, EISCONN }, { WSAEOPNOTSUPP, EOPNOTSUPP }, { WSAEPROTOTYPE, EPROTOTYPE }, { WSAENOPROTOOPT, ENOPROTOOPT }, { WSAEPROTONOSUPPORT, EPROTONOSUPPORT }, { WSAEPFNOSUPPORT, EPROTONOSUPPORT }, { WSAEAFNOSUPPORT, EAFNOSUPPORT }, { WSAEADDRNOTAVAIL, EADDRNOTAVAIL }, { WSAEADDRINUSE, EADDRINUSE }, { WSAETIMEDOUT, ETIMEDOUT }, { WSAECONNREFUSED, ECONNREFUSED }, { WSAELOOP, ELOOP }, { WSAENAMETOOLONG, ENAMETOOLONG }, { WSAENOTEMPTY, ENOTEMPTY }, { WSAEPROTOTYPE, EPROTOTYPE }, { WSAVERNOTSUPPORTED, ENOTSUP }, }; DWORD wsa, i; wsa = WSAGetLastError(); for (i = 0; i < countof(map); i++) { if (map[i].wsa == wsa) { errno = map[i].err; return retval; } } errno = ENOENT; return retval; } errno = 0; return retval; } /** * Check and clear the dontwait flag */ static bool check_dontwait(int *flags) { if (*flags & MSG_DONTWAIT) { *flags &= ~MSG_DONTWAIT; return TRUE; } return FALSE; } /** * See header */ #undef shutdown int windows_shutdown(int sockfd, int how) { return wserr(shutdown(sockfd, how)); } /** * See header */ #undef accept int windows_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { return wserr(accept(sockfd, addr, addrlen)); } /** * See header */ #undef bind int windows_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { return wserr(bind(sockfd, addr, addrlen)); } /** * See header */ #undef connect int windows_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { return wserr(connect(sockfd, addr, addrlen)); } /** * See header */ #undef getsockname int windows_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { return wserr(getsockname(sockfd, addr, addrlen)); } /** * See header */ #undef getsockopt int windows_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { return wserr(getsockopt(sockfd, level, optname, optval, optlen)); } /** * See header */ #undef setsockopt int windows_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { return wserr(setsockopt(sockfd, level, optname, optval, optlen)); } /** * See header */ #undef socket int windows_socket(int domain, int type, int protocol) { return wserr(socket(domain, type, protocol)); } /** * See header */ #undef select int windows_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { return wserr(select(nfds, readfds, writefds, exceptfds, timeout)); } /** * See header */ #undef close int windows_close(int fd) { int ret; ret = close(fd); if (ret == -1 && errno == EBADF) { /* Winsock socket? */ ret = wserr(closesocket(fd)); } return ret; }
int main(int argc, char** argv) { int error; #ifdef _WIN32 WSADATA wsaData; error = WSAStartup(MAKEWORD(2,2), &wsaData); if (error) { err(EX_UNAVAILABLE, "Unable to start Winsock"); } #endif program_name = basename(argv[0]); if (argc != 4) { usage(); exit(EX_USAGE); } struct addrinfo *res, hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; error = getaddrinfo(argv[1], argv[2], &hints, &res); if (error) { wserr(EX_NOHOST, "getaddrinfo(%s, %s) failed", argv[1], argv[2]); } int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (-1 == fd) { wserr(EX_UNAVAILABLE, "Unable to create socket"); } error = connect(fd, res->ai_addr, res->ai_addrlen); if (error) { wserr(EX_UNAVAILABLE, "connect() failed"); } char* msg = argv[3]; int msglen = strlen(msg); int bytes_written = send(fd, msg, msglen, 0); if (bytes_written < 0) { wserr(EX_IOERR, "send() failed"); } else if (bytes_written < msglen) { wserr(EX_IOERR, "send() only wrote %d of %d bytes", bytes_written, msglen); } /* Unfortunately, half-closing the socket crashes the tcpr server. */ /* error = shutdown(fd, SHUT_WR); if (error) { wserr(EX_IOERR, "shutdown socket for writing failed"); } */ char buffer[1024]; int bytes_read; while ((bytes_read = recv(fd, buffer, sizeof(buffer), 0)) > 0) { int i; for (i = 0 ; i < bytes_read; i++) putchar(buffer[i]); } if (bytes_read < 0) { wserr(EX_IOERR, "recv() failed"); } return 0; }