/* * Generate a management command from user input and send it */ BOOL ManagementCommandFromInput(connection_t *c, LPSTR fmt, HWND hDlg, int id) { BOOL retval = FALSE; LPSTR input, cmd; int input_len, cmd_len; GetDlgItemTextUtf8(hDlg, id, &input, &input_len); cmd_len = input_len + strlen(fmt); cmd = malloc(cmd_len); if (cmd) { snprintf(cmd, cmd_len, fmt, input); retval = ManagementCommand(c, cmd, NULL, regular); free(cmd); } /* Clear buffers with potentially secret content */ memset(input, 'x', input_len - 1); SetDlgItemTextA(hDlg, id, input); free(input); return retval; }
/* * Handle management socket events asynchronously */ void OnManagement(SOCKET sk, LPARAM lParam) { int res; char *pos = NULL; char data[MAX_LOG_LENGTH]; connection_t *c = GetConnByManagement(sk); if (c == NULL) return; switch (WSAGETSELECTEVENT(lParam)) { case FD_CONNECT: if (WSAGETSELECTERROR(lParam)) SendMessage(c->hwndStatus, WM_CLOSE, 0, 0); break; case FD_READ: /* Check if there's a complete line to read */ res = recv(c->manage.sk, data, sizeof(data), MSG_PEEK); if (res < 1) return; pos = memchr(data, (*c->manage.password ? ':' : '\n'), res); if (!pos) return; /* There is data available: read it */ res = recv(c->manage.sk, data, pos - data + 1, 0); if (res != pos - data + 1) return; /* Reply to a management password request */ if (*c->manage.password) { ManagementCommand(c, c->manage.password, NULL, regular); *c->manage.password = '******'; return; } /* Handle regular management interface output */ data[pos - data - 1] = '\0'; if (data[0] == '>') { /* Real time notifications */ pos = data + 1; if (strncmp(pos, "LOG:", 4) == 0) { if (rtmsg_handler[log]) rtmsg_handler[log](c, pos + 4); } else if (strncmp(pos, "STATE:", 6) == 0) { if (rtmsg_handler[state]) rtmsg_handler[state](c, pos + 6); } else if (strncmp(pos, "HOLD:", 5) == 0) { if (rtmsg_handler[hold]) rtmsg_handler[hold](c, pos + 5); } else if (strncmp(pos, "PASSWORD:"******"INFO:", 5) == 0) { /* delay until management interface accepts input */ Sleep(100); if (rtmsg_handler[ready]) rtmsg_handler[ready](c, pos + 5); } } else if (c->manage.cmd_queue) { /* Response to commands */ mgmt_cmd_t *cmd = c->manage.cmd_queue; if (strncmp(data, "SUCCESS:", 8) == 0) { if (cmd->handler) cmd->handler(c, data + 9); UnqueueCommand(c); } else if (strncmp(data, "ERROR:", 6) == 0) { if (cmd->handler) cmd->handler(c, NULL); UnqueueCommand(c); } else if (strcmp(data, "END") == 0) { UnqueueCommand(c); } else if (cmd->handler) { cmd->handler(c, data); } } break; case FD_WRITE: SendCommand(c); break; case FD_CLOSE: closesocket(c->manage.sk); c->manage.sk = INVALID_SOCKET; while (UnqueueCommand(c)) ; WSACleanup(); if (rtmsg_handler[stop]) rtmsg_handler[stop](c, ""); break; } }
/* * Handle the request to release a hold from the OpenVPN management interface */ void OnHold(connection_t *c, UNUSED char *msg) { ManagementCommand(c, "hold off", NULL, regular); ManagementCommand(c, "hold release", NULL, regular); }
/* * DialogProc for OpenVPN status dialog windows */ INT_PTR CALLBACK StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { connection_t *c; switch (msg) { case WM_MANAGEMENT: /* Management interface related event */ OnManagement(wParam, lParam); return TRUE; case WM_INITDIALOG: c = (connection_t *) lParam; /* Set window icon "disconnected" */ SetStatusWinIcon(hwndDlg, ID_ICO_CONNECTING); /* Set connection for this dialog */ SetProp(hwndDlg, cfgProp, (HANDLE) c); /* Create log window */ HWND hLogWnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL, WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|ES_SUNKEN|ES_LEFT| ES_MULTILINE|ES_READONLY|ES_AUTOHSCROLL|ES_AUTOVSCROLL, 20, 25, 350, 160, hwndDlg, (HMENU) ID_EDT_LOG, o.hInstance, NULL); if (!hLogWnd) { ShowLocalizedMsg(IDS_ERR_CREATE_EDIT_LOGWINDOW); return FALSE; } /* Set font and fontsize of the log window */ CHARFORMAT cfm = { .cbSize = sizeof(CHARFORMAT), .dwMask = CFM_SIZE|CFM_FACE|CFM_BOLD, .szFaceName = _T("Microsoft Sans Serif"), .dwEffects = 0, .yHeight = 160 }; if (SendMessage(hLogWnd, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfm) == 0) ShowLocalizedMsg(IDS_ERR_SET_SIZE); /* Set size and position of controls */ RECT rect; GetClientRect(hwndDlg, &rect); MoveWindow(hLogWnd, 20, 25, rect.right - 40, rect.bottom - 70, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_TXT_STATUS), 20, 5, rect.right - 25, 15, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_DISCONNECT), 20, rect.bottom - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_RESTART), 145, rect.bottom - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_HIDE), rect.right - 130, rect.bottom - 30, 110, 23, TRUE); /* Set focus on the LogWindow so it scrolls automatically */ SetFocus(hLogWnd); return FALSE; case WM_SIZE: MoveWindow(GetDlgItem(hwndDlg, ID_EDT_LOG), 20, 25, LOWORD(lParam) - 40, HIWORD(lParam) - 70, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_DISCONNECT), 20, HIWORD(lParam) - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_RESTART), 145, HIWORD(lParam) - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_HIDE), LOWORD(lParam) - 130, HIWORD(lParam) - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_TXT_STATUS), 20, 5, LOWORD(lParam) - 25, 15, TRUE); InvalidateRect(hwndDlg, NULL, TRUE); return TRUE; case WM_COMMAND: c = (connection_t *) GetProp(hwndDlg, cfgProp); switch (LOWORD(wParam)) { case ID_DISCONNECT: SetFocus(GetDlgItem(c->hwndStatus, ID_EDT_LOG)); StopOpenVPN(c); return TRUE; case ID_HIDE: if (c->state != disconnected) ShowWindow(hwndDlg, SW_HIDE); else DestroyWindow(hwndDlg); return TRUE; case ID_RESTART: c->state = reconnecting; SetFocus(GetDlgItem(c->hwndStatus, ID_EDT_LOG)); ManagementCommand(c, "signal SIGHUP", NULL, regular); return TRUE; } break; case WM_SHOWWINDOW: if (wParam == TRUE) { c = (connection_t *) GetProp(hwndDlg, cfgProp); if (c->hwndStatus) SetFocus(GetDlgItem(c->hwndStatus, ID_EDT_LOG)); } return FALSE; case WM_CLOSE: c = (connection_t *) GetProp(hwndDlg, cfgProp); if (c->state != disconnected) ShowWindow(hwndDlg, SW_HIDE); else DestroyWindow(hwndDlg); return TRUE; case WM_NCDESTROY: RemoveProp(hwndDlg, cfgProp); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_OVPN_STOP: c = (connection_t *) GetProp(hwndDlg, cfgProp); c->state = disconnecting; RunDisconnectScript(c, false); EnableWindow(GetDlgItem(c->hwndStatus, ID_DISCONNECT), FALSE); EnableWindow(GetDlgItem(c->hwndStatus, ID_RESTART), FALSE); SetMenuStatus(c, disconnecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_WAIT_TERM)); SetEvent(c->exit_event); SetTimer(hwndDlg, IDT_STOP_TIMER, 3000, NULL); break; case WM_OVPN_SUSPEND: c = (connection_t *) GetProp(hwndDlg, cfgProp); c->state = suspending; EnableWindow(GetDlgItem(c->hwndStatus, ID_DISCONNECT), FALSE); EnableWindow(GetDlgItem(c->hwndStatus, ID_RESTART), FALSE); SetMenuStatus(c, disconnecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_WAIT_TERM)); SetEvent(c->exit_event); SetTimer(hwndDlg, IDT_STOP_TIMER, 3000, NULL); break; case WM_TIMER: PrintDebug(L"WM_TIMER message with wParam = %lu", wParam); c = (connection_t *) GetProp(hwndDlg, cfgProp); if (wParam == IDT_STOP_TIMER) { /* openvpn failed to respond to stop signal -- terminate */ TerminateOpenVPN(c); KillTimer (hwndDlg, IDT_STOP_TIMER); } break; } return FALSE; } /* * ThreadProc for OpenVPN status dialog windows */ static DWORD WINAPI ThreadOpenVPNStatus(void *p) { connection_t *c = p; TCHAR conn_name[200]; MSG msg; HANDLE wait_event; CLEAR (msg); /* Cut of extention from config filename. */ _tcsncpy(conn_name, c->config_file, _countof(conn_name)); conn_name[_tcslen(conn_name) - _tcslen(o.ext_string) - 1] = _T('\0'); c->state = (c->state == suspended ? resuming : connecting); /* Create and Show Status Dialog */ c->hwndStatus = CreateLocalizedDialogParam(ID_DLG_STATUS, StatusDialogFunc, (LPARAM) c); if (!c->hwndStatus) return 1; CheckAndSetTrayIcon(); SetMenuStatus(c, connecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_CONNECTING)); SetWindowText(c->hwndStatus, LoadLocalizedString(IDS_NFO_CONNECTION_XXX, conn_name)); if (!OpenManagement(c)) PostMessage(c->hwndStatus, WM_CLOSE, 0, 0); /* Start the async read loop for service and set it as the wait event */ if (c->iserv.hEvent) { HandleServiceIO (0, 0, (LPOVERLAPPED) &c->iserv); wait_event = c->iserv.hEvent; } else wait_event = c->hProcess; if (o.silent_connection == 0) ShowWindow(c->hwndStatus, SW_SHOW); /* Run the message loop for the status window */ while (WM_QUIT != msg.message) { DWORD res; if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if ((res = MsgWaitForMultipleObjectsEx (1, &wait_event, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE)) == WAIT_OBJECT_0) { if (wait_event == c->hProcess) OnProcess (c, NULL); else if (wait_event == c->iserv.hEvent) OnService (c, NULL); } continue; } if (IsDialogMessage(c->hwndStatus, &msg) == 0) { TranslateMessage(&msg); DispatchMessage(&msg); } } /* release handles etc.*/ Cleanup (c); c->hwndStatus = NULL; return 0; }
/* * Receive banner on connection to management interface * Format: <BANNER> */ void OnReady(connection_t *c, UNUSED char *msg) { ManagementCommand(c, "state on", NULL, regular); ManagementCommand(c, "log all on", OnLogLine, combined); }
/* * DialogProc for OpenVPN status dialog windows */ INT_PTR CALLBACK StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { connection_t *c; switch (msg) { case WM_MANAGEMENT: /* Management interface related event */ OnManagement(wParam, lParam); return TRUE; case WM_INITDIALOG: c = (connection_t *) lParam; /* Set window icon "disconnected" */ SetStatusWinIcon(hwndDlg, ID_ICO_CONNECTING); /* Set connection for this dialog */ SetProp(hwndDlg, cfgProp, (HANDLE) c); /* Create log window */ HWND hLogWnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL, WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|ES_SUNKEN|ES_LEFT| ES_MULTILINE|ES_READONLY|ES_AUTOHSCROLL|ES_AUTOVSCROLL, 20, 25, 350, 160, hwndDlg, (HMENU) ID_EDT_LOG, o.hInstance, NULL); if (!hLogWnd) { ShowLocalizedMsg(IDS_ERR_CREATE_EDIT_LOGWINDOW); return FALSE; } /* Set font and fontsize of the log window */ CHARFORMAT cfm = { .cbSize = sizeof(CHARFORMAT), .dwMask = CFM_SIZE|CFM_FACE|CFM_BOLD, .szFaceName = _T("Microsoft Sans Serif"), .dwEffects = 0, .yHeight = 160 }; if (SendMessage(hLogWnd, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfm) == 0) ShowLocalizedMsg(IDS_ERR_SET_SIZE); /* Set size and position of controls */ RECT rect; GetClientRect(hwndDlg, &rect); MoveWindow(hLogWnd, 20, 25, rect.right - 40, rect.bottom - 70, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_TXT_STATUS), 20, 5, rect.right - 25, 15, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_DISCONNECT), 20, rect.bottom - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_RESTART), 145, rect.bottom - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_HIDE), rect.right - 130, rect.bottom - 30, 110, 23, TRUE); /* Set focus on the LogWindow so it scrolls automatically */ SetFocus(hLogWnd); return FALSE; case WM_SIZE: MoveWindow(GetDlgItem(hwndDlg, ID_EDT_LOG), 20, 25, LOWORD(lParam) - 40, HIWORD(lParam) - 70, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_DISCONNECT), 20, HIWORD(lParam) - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_RESTART), 145, HIWORD(lParam) - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_HIDE), LOWORD(lParam) - 130, HIWORD(lParam) - 30, 110, 23, TRUE); MoveWindow(GetDlgItem(hwndDlg, ID_TXT_STATUS), 20, 5, LOWORD(lParam) - 25, 15, TRUE); InvalidateRect(hwndDlg, NULL, TRUE); return TRUE; case WM_COMMAND: c = (connection_t *) GetProp(hwndDlg, cfgProp); switch (LOWORD(wParam)) { case ID_DISCONNECT: SetFocus(GetDlgItem(c->hwndStatus, ID_EDT_LOG)); StopOpenVPN(c); return TRUE; case ID_HIDE: if (c->state != disconnected) ShowWindow(hwndDlg, SW_HIDE); else DestroyWindow(hwndDlg); return TRUE; case ID_RESTART: c->state = reconnecting; SetFocus(GetDlgItem(c->hwndStatus, ID_EDT_LOG)); ManagementCommand(c, "signal SIGHUP", NULL, regular); return TRUE; } break; case WM_SHOWWINDOW: if (wParam == TRUE) { c = (connection_t *) GetProp(hwndDlg, cfgProp); if (c->hwndStatus) SetFocus(GetDlgItem(c->hwndStatus, ID_EDT_LOG)); } return FALSE; case WM_CLOSE: c = (connection_t *) GetProp(hwndDlg, cfgProp); if (c->state != disconnected) ShowWindow(hwndDlg, SW_HIDE); else DestroyWindow(hwndDlg); return TRUE; case WM_NCDESTROY: RemoveProp(hwndDlg, cfgProp); break; case WM_DESTROY: PostQuitMessage(0); break; } return FALSE; } /* * ThreadProc for OpenVPN status dialog windows */ static DWORD WINAPI ThreadOpenVPNStatus(void *p) { connection_t *c = p; TCHAR conn_name[200]; MSG msg; /* Cut of extention from config filename. */ _tcsncpy(conn_name, c->config_file, _countof(conn_name)); conn_name[_tcslen(conn_name) - _tcslen(o.ext_string) - 1] = _T('\0'); c->state = (c->state == suspended ? resuming : connecting); /* Create and Show Status Dialog */ c->hwndStatus = CreateLocalizedDialogParam(ID_DLG_STATUS, StatusDialogFunc, (LPARAM) c); if (!c->hwndStatus) return 1; CheckAndSetTrayIcon(); SetMenuStatus(c, connecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_CONNECTING)); SetWindowText(c->hwndStatus, LoadLocalizedString(IDS_NFO_CONNECTION_XXX, conn_name)); if (!OpenManagement(c)) PostMessage(c->hwndStatus, WM_CLOSE, 0, 0); if (o.silent_connection[0] == '0') ShowWindow(c->hwndStatus, SW_SHOW); /* Run the message loop for the status window */ while (GetMessage(&msg, NULL, 0, 0)) { if (msg.hwnd == NULL) { switch (msg.message) { case WM_OVPN_STOP: c->state = disconnecting; RunDisconnectScript(c, false); EnableWindow(GetDlgItem(c->hwndStatus, ID_DISCONNECT), FALSE); EnableWindow(GetDlgItem(c->hwndStatus, ID_RESTART), FALSE); SetMenuStatus(c, disconnecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_WAIT_TERM)); SetEvent(c->exit_event); break; case WM_OVPN_SUSPEND: c->state = suspending; EnableWindow(GetDlgItem(c->hwndStatus, ID_DISCONNECT), FALSE); EnableWindow(GetDlgItem(c->hwndStatus, ID_RESTART), FALSE); SetMenuStatus(&o.conn[config], disconnecting); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_WAIT_TERM)); SetEvent(c->exit_event); break; } } else if (IsDialogMessage(c->hwndStatus, &msg) == 0) { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0; }
/* * Handle a state change notification from the OpenVPN management interface * Format <TIMESTAMP>,<STATE>,[<MESSAGE>],[<LOCAL_IP>][,<REMOTE_IP>] */ void OnStateChange(connection_t *c, char *data) { char *pos, *state, *message; pos = strchr(data, ','); if (pos == NULL) return; *pos = '\0'; state = pos + 1; pos = strchr(state, ','); if (pos == NULL) return; *pos = '\0'; message = pos + 1; pos = strchr(message, ','); if (pos == NULL) return; *pos = '\0'; if (strcmp(state, "CONNECTED") == 0) { /* Run Connect Script */ if (c->state == connecting || c->state == resuming) RunConnectScript(c, false); /* Save the local IP address if available */ char *local_ip = pos + 1; pos = strchr(local_ip, ','); if (pos != NULL) *pos = '\0'; /* Convert the IP address to Unicode */ MultiByteToWideChar(CP_ACP, 0, local_ip, -1, c->ip, _countof(c->ip)); /* Show connection tray balloon */ if ((c->state == connecting && o.show_balloon[0] != '0') || (c->state == resuming && o.show_balloon[0] != '0') || (c->state == reconnecting && o.show_balloon[0] == '2')) { TCHAR msg[256]; LoadLocalizedStringBuf(msg, _countof(msg), IDS_NFO_NOW_CONNECTED, c->config_name); ShowTrayBalloon(msg, (_tcslen(c->ip) ? LoadLocalizedString(IDS_NFO_ASSIGN_IP, c->ip) : _T(""))); } /* Save time when we got connected. */ c->connected_since = atoi(data); c->failed_psw_attempts = 0; c->state = connected; SetMenuStatus(c, connected); SetTrayIcon(connected); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_CONNECTED)); SetStatusWinIcon(c->hwndStatus, ID_ICO_CONNECTED); /* Hide Status Window */ ShowWindow(c->hwndStatus, SW_HIDE); } else if (strcmp(state, "RECONNECTING") == 0) { if (strcmp(message, "auth-failure") == 0 || strcmp(message, "private-key-password-failure") == 0) c->failed_psw_attempts++; if (c->failed_psw_attempts >= o.psw_attempts - 1) ManagementCommand(c, "auth-retry none", NULL, regular); c->state = reconnecting; CheckAndSetTrayIcon(); SetDlgItemText(c->hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_RECONNECTING)); SetStatusWinIcon(c->hwndStatus, ID_ICO_CONNECTING); } }
/* * Handle management socket events asynchronously */ void OnManagement(SOCKET sk, LPARAM lParam) { int res; char *data; ULONG data_size, offset; connection_t *c = GetConnByManagement(sk); if (c == NULL) return; switch (WSAGETSELECTEVENT(lParam)) { case FD_CONNECT: if (WSAGETSELECTERROR(lParam)) { if (time(NULL) < c->manage.timeout) connect(c->manage.sk, (SOCKADDR *)&c->manage.skaddr, sizeof(c->manage.skaddr)); else { /* Connection to MI timed out. */ c->state = timedout; rtmsg_handler[stop](c, ""); } } break; case FD_READ: if (ioctlsocket(c->manage.sk, FIONREAD, &data_size) != 0 || data_size == 0) return; data = malloc(c->manage.saved_size + data_size); if (data == NULL) return; res = recv(c->manage.sk, data + c->manage.saved_size, data_size, 0); if (res != (int) data_size) { free(data); return; } /* Copy previously saved management data */ if (c->manage.saved_size) { memcpy(data, c->manage.saved_data, c->manage.saved_size); data_size += c->manage.saved_size; free(c->manage.saved_data); c->manage.saved_data = NULL; c->manage.saved_size = 0; } offset = 0; while (offset < data_size) { char *pos; char *line = data + offset; size_t line_size = data_size - offset; pos = memchr(line, (*c->manage.password ? ':' : '\n'), line_size); if (pos == NULL) { c->manage.saved_data = malloc(line_size); if (c->manage.saved_data) { c->manage.saved_size = line_size; memcpy(c->manage.saved_data, line, c->manage.saved_size); } break; } offset += (pos - line) + 1; /* Reply to a management password request */ if (*c->manage.password) { ManagementCommand(c, c->manage.password, NULL, regular); *c->manage.password = '******'; continue; } /* Handle regular management interface output */ line[pos - line - 1] = '\0'; if (line[0] == '>') { /* Real time notifications */ pos = line + 1; if (strncmp(pos, "LOG:", 4) == 0) { if (rtmsg_handler[log]) rtmsg_handler[log](c, pos + 4); } else if (strncmp(pos, "STATE:", 6) == 0) { if (rtmsg_handler[state]) rtmsg_handler[state](c, pos + 6); } else if (strncmp(pos, "HOLD:", 5) == 0) { if (rtmsg_handler[hold]) rtmsg_handler[hold](c, pos + 5); } else if (strncmp(pos, "PASSWORD:"******"PROXY:", 6) == 0) { if (rtmsg_handler[proxy]) rtmsg_handler[proxy](c, pos + 6); } else if (strncmp(pos, "INFO:", 5) == 0) { /* delay until management interface accepts input */ Sleep(100); if (rtmsg_handler[ready]) rtmsg_handler[ready](c, pos + 5); } } else if (c->manage.cmd_queue) { /* Response to commands */ mgmt_cmd_t *cmd = c->manage.cmd_queue; if (strncmp(line, "SUCCESS:", 8) == 0) { if (cmd->handler) cmd->handler(c, line + 9); UnqueueCommand(c); } else if (strncmp(line, "ERROR:", 6) == 0) { if (cmd->handler) cmd->handler(c, NULL); UnqueueCommand(c); } else if (strcmp(line, "END") == 0) { UnqueueCommand(c); } else if (cmd->handler) { cmd->handler(c, line); } } } free(data); break; case FD_WRITE: SendCommand(c); break; case FD_CLOSE: if (c->manage.saved_size) { free(c->manage.saved_data); c->manage.saved_data = NULL; c->manage.saved_size = 0; } closesocket(c->manage.sk); c->manage.sk = INVALID_SOCKET; while (UnqueueCommand(c)) ; WSACleanup(); if (rtmsg_handler[stop]) rtmsg_handler[stop](c, ""); break; } }
static LPWSTR QueryWindowsProxySettings(const url_scheme scheme, LPCSTR host) { LPWSTR proxy = NULL; BOOL auto_detect = TRUE; LPWSTR auto_config_url = NULL; WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config; if (WinHttpGetIEProxyConfigForCurrentUser(&proxy_config)) { proxy = proxy_config.lpszProxy; auto_detect = proxy_config.fAutoDetect; auto_config_url = proxy_config.lpszAutoConfigUrl; GlobalFree(proxy_config.lpszProxyBypass); } if (auto_detect) { LPWSTR old_url = auto_config_url; DWORD flags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; if (WinHttpDetectAutoProxyConfigUrl(flags, &auto_config_url)) GlobalFree(old_url); } if (auto_config_url) { HINTERNET session = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (session) { int size = _snwprintf(NULL, 0, L"%s://%S", UrlSchemeStr(scheme), host) + 1; LPWSTR url = malloc(size * sizeof(WCHAR)); if (url) { _snwprintf(url, size, L"%s://%S", UrlSchemeStr(scheme), host); LPWSTR old_proxy = proxy; WINHTTP_PROXY_INFO proxy_info; WINHTTP_AUTOPROXY_OPTIONS options = { .fAutoLogonIfChallenged = TRUE, .dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL, .lpszAutoConfigUrl = auto_config_url, .dwAutoDetectFlags = 0, .lpvReserved = NULL, .dwReserved = 0 }; if (WinHttpGetProxyForUrl(session, url, &options, &proxy_info)) { GlobalFree(old_proxy); GlobalFree(proxy_info.lpszProxyBypass); proxy = proxy_info.lpszProxy; } free(url); } WinHttpCloseHandle(session); } GlobalFree(auto_config_url); } return proxy; } static VOID ParseProxyString(LPWSTR proxy_str, url_scheme scheme, LPCSTR *type, LPCWSTR *host, LPCWSTR *port) { if (proxy_str == NULL) return; LPCWSTR delim = L"; "; LPWSTR token = wcstok(proxy_str, delim); LPCWSTR scheme_str = UrlSchemeStr(scheme); LPCWSTR socks_str = UrlSchemeStr(SOCKS_URL); /* Token format: [<scheme>=][<scheme>"://"]<server>[":"<port>] */ while (token) { BOOL match = FALSE; LPWSTR eq = wcschr(token, '='); LPWSTR css = wcsstr(token, L"://"); /* * If the token has a <scheme>, test for the one we're looking for. * If we're looking for a https proxy, socks will also do. * If it's a proxy without a <scheme> it's only good for https. */ if (eq || css) { if (wcsbegins(token, scheme_str)) { match = TRUE; } else if (scheme == HTTPS_URL && wcsbegins(token, socks_str)) { match = TRUE; scheme = SOCKS_URL; } } else if (scheme == HTTPS_URL) { match = TRUE; } if (match) { LPWSTR server = token; if (css) server = css + 3; else if (eq) server = eq + 1; /* IPv6 addresses are surrounded by brackets */ LPWSTR port_delim; if (server[0] == '[') { server += 1; LPWSTR end = wcschr(server, ']'); if (end == NULL) continue; *end++ = '\0'; port_delim = (*end == ':' ? end : NULL); } else { port_delim = wcsrchr(server, ':'); if (port_delim) *port_delim = '\0'; } *type = (scheme == HTTPS_URL ? "HTTP" : "SOCKS"); *host = server; if (port_delim) *port = port_delim + 1; else *port = (scheme == HTTPS_URL ? L"80": L"1080"); break; } token = wcstok(NULL, delim); } } /* * Respond to management interface PROXY notifications * Input format: REMOTE_NO,PROTOCOL,HOST */ void OnProxy(connection_t *c, char *line) { LPSTR proto, host; char *pos = strchr(line, ','); if (pos == NULL) return; proto = ++pos; pos = strchr(pos, ','); if (pos == NULL) return; *pos = '\0'; host = ++pos; if (host[0] == '\0') return; LPCSTR type = "NONE"; LPCWSTR addr = L"", port = L""; LPWSTR proxy_str = NULL; if (o.proxy_source == manual) { if (o.proxy_type == http && streq(proto, "TCP")) { type = "HTTP"; addr = o.proxy_http_address; port = o.proxy_http_port; } else if (o.proxy_type == socks) { type = "SOCKS"; addr = o.proxy_socks_address; port = o.proxy_socks_port; } } else if (o.proxy_source == windows) { url_scheme scheme = (streq(proto, "TCP") ? HTTPS_URL : SOCKS_URL); proxy_str = QueryWindowsProxySettings(scheme, host); ParseProxyString(proxy_str, scheme, &type, &addr, &port); } char cmd[128]; snprintf(cmd, sizeof(cmd), "proxy %s %S %S", type, addr, port); cmd[sizeof(cmd) - 1] = '\0'; ManagementCommand(c, cmd, NULL, regular); GlobalFree(proxy_str); }