You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

245 lines
5.8 KiB

/*
* winsock.c - Windows socket compatibility layer
*
* Copyright (C) 2013 - 2018, Max Lv <max.c.lv@gmail.com>
*
* This file is part of the shadowsocks-libev.
*
* shadowsocks-libev is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* shadowsocks-libev is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with shadowsocks-libev; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifdef __MINGW32__
#include "winsock.h"
#include "utils.h"
#ifndef ENABLE_QUICK_EDIT
#define ENABLE_QUICK_EDIT 0x0040
#endif
#ifndef STD_INPUT_HANDLE
#define STD_INPUT_HANDLE ((DWORD)-10)
#endif
#ifndef ENABLE_EXTENDED_FLAGS
#define ENABLE_EXTENDED_FLAGS 0x0080
#endif
#ifndef STD_OUTPUT_HANDLE
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#endif
static void
disable_quick_edit(void)
{
DWORD mode = 0;
HANDLE console = GetStdHandle(STD_INPUT_HANDLE);
// Get current console mode
if (console == NULL ||
console == INVALID_HANDLE_VALUE ||
!GetConsoleMode(console, &mode)) {
return;
}
// Clear the quick edit bit in the mode flags
mode &= ~ENABLE_QUICK_EDIT;
mode |= ENABLE_EXTENDED_FLAGS;
SetConsoleMode(console, mode);
}
void
winsock_init(void)
{
int ret;
WSADATA wsa_data;
ret = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (ret != 0) {
FATAL("Failed to initialize winsock");
}
// Disable quick edit mode to prevent stuck
disable_quick_edit();
}
void
winsock_cleanup(void)
{
WSACleanup();
}
int
setnonblocking(SOCKET socket)
{
u_long arg = 1;
return ioctlsocket(socket, FIONBIO, &arg);
}
void
ss_error(const char *s)
{
char *msg = NULL;
DWORD err = WSAGetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&msg, 0, NULL);
if (msg != NULL) {
// Remove trailing newline character
ssize_t len = strlen(msg) - 1;
if (len >= 0 && msg[len] == '\n') {
msg[len] = '\0';
}
LOGE("%s: [%ld] %s", s, err, msg);
LocalFree(msg);
}
}
char *
ss_gai_strerror(int ecode)
{
static TCHAR buff[GAI_STRERROR_BUFFER_SIZE + 1];
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL, ecode,
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)buff, GAI_STRERROR_BUFFER_SIZE, NULL);
return (char *)buff;
}
static BOOL
get_conattr(HANDLE console, WORD *out_attr)
{
static BOOL done = FALSE;
static WORD saved_attr = 0;
if (!done) {
CONSOLE_SCREEN_BUFFER_INFO info;
if (GetConsoleScreenBufferInfo(console, &info)) {
saved_attr = info.wAttributes;
done = TRUE;
}
}
if (out_attr != NULL) {
*out_attr = saved_attr;
}
return done;
}
static BOOL
set_concolor(WORD color, BOOL reset)
{
static HANDLE console = NULL;
if (console == NULL) {
console = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (console == NULL ||
console == INVALID_HANDLE_VALUE) {
// If no console is available, we will not try again
console = INVALID_HANDLE_VALUE;
return FALSE;
}
WORD attr;
if (!get_conattr(console, &attr)) {
return FALSE;
}
if (!reset) {
// Only override foreground color without changing background
attr &= ~(FOREGROUND_RED | FOREGROUND_GREEN |
FOREGROUND_BLUE | FOREGROUND_INTENSITY);
attr |= (color | FOREGROUND_INTENSITY);
}
return SetConsoleTextAttribute(console, attr);
}
void
ss_color_info(void)
{
set_concolor(FOREGROUND_GREEN, FALSE);
}
void
ss_color_error(void)
{
set_concolor(FOREGROUND_RED | FOREGROUND_BLUE, FALSE);
}
void
ss_color_reset(void)
{
set_concolor(0, TRUE);
}
#ifdef TCP_FASTOPEN_WINSOCK
LPFN_CONNECTEX
winsock_getconnectex(void)
{
static LPFN_CONNECTEX pConnectEx = NULL;
if (pConnectEx != NULL) {
return pConnectEx;
}
// Dummy socket for WSAIoctl
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
ERROR("socket");
return NULL;
}
// Load ConnectEx function
GUID guid = WSAID_CONNECTEX;
DWORD numBytes;
int ret = -1;
ret = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER,
(void *)&guid, sizeof(guid),
(void *)&pConnectEx, sizeof(pConnectEx),
&numBytes, NULL, NULL);
if (ret != 0) {
ERROR("WSAIoctl");
closesocket(s);
return NULL;
}
closesocket(s);
return pConnectEx;
}
int
winsock_dummybind(SOCKET fd, struct sockaddr *sa)
{
struct sockaddr_storage ss;
memset(&ss, 0, sizeof(ss));
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = INADDR_ANY;
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = in6addr_any;
} else {
return -1;
}
if (bind(fd, (struct sockaddr *)&ss, sizeof(ss)) < 0 &&
WSAGetLastError() != WSAEINVAL) {
return -1;
}
return 0;
}
#endif
#endif // __MINGW32__