mirror of https://github.com/OpenVPN/openvpn-gui
				
				
				
			
		
			
				
	
	
		
			308 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
/*
 | 
						|
 *  OpenVPN-GUI -- A Windows GUI for OpenVPN.
 | 
						|
 *
 | 
						|
 *  Copyright (C) 2010 Heiko Hund <heikoh@users.sf.net>
 | 
						|
 *
 | 
						|
 *  This program 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 2 of the License, or
 | 
						|
 *  (at your option) any later version.
 | 
						|
 *
 | 
						|
 *  This program 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 this program (see the file COPYING included with this
 | 
						|
 *  distribution); if not, write to the Free Software Foundation, Inc.,
 | 
						|
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
 */
 | 
						|
 | 
						|
#include <Winsock2.h>
 | 
						|
 | 
						|
#include "options.h"
 | 
						|
#include "main.h"
 | 
						|
#include "manage.h"
 | 
						|
 | 
						|
extern options_t o;
 | 
						|
 | 
						|
static mgmt_msg_func rtmsg_handler[mgmt_rtmsg_type_max];
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Initialize the real-time notification handlers
 | 
						|
 */
 | 
						|
void
 | 
						|
InitManagement(const mgmt_rtmsg_handler *handler)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    for (i = 0; handler[i].handler; ++i)
 | 
						|
    {
 | 
						|
        rtmsg_handler[handler[i].type] = handler[i].handler;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Connect to the OpenVPN management interface and register
 | 
						|
 * asynchronous socket event notification for it
 | 
						|
 */
 | 
						|
BOOL
 | 
						|
OpenManagement(connection_t *c, u_long addr, u_short port)
 | 
						|
{
 | 
						|
    WSADATA wsaData;
 | 
						|
    SOCKADDR_IN skaddr = {
 | 
						|
        .sin_family = AF_INET,
 | 
						|
        .sin_addr.s_addr = addr,
 | 
						|
        .sin_port = htons(port)
 | 
						|
    };
 | 
						|
 | 
						|
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    c->manage.sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 | 
						|
    if (c->manage.sk == INVALID_SOCKET)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    if (WSAAsyncSelect(c->manage.sk, c->hwndStatus, WM_MANAGEMENT,
 | 
						|
        FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE) != 0)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    connect(c->manage.sk, (SOCKADDR *)&skaddr, sizeof(skaddr));
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Send a command to the OpenVPN management interface
 | 
						|
 */
 | 
						|
BOOL
 | 
						|
ManagementCommand(connection_t *c, char *command, mgmt_msg_func handler, mgmt_cmd_type type)
 | 
						|
{
 | 
						|
    int res = 0;
 | 
						|
    int size = strlen(command) + 1;
 | 
						|
 | 
						|
    mgmt_cmd_t *cmd = calloc(1, sizeof(*cmd));
 | 
						|
    if (cmd == NULL)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    cmd->handler = handler;
 | 
						|
    cmd->type = type;
 | 
						|
 | 
						|
    if (c->manage.cmd_queue)
 | 
						|
    {
 | 
						|
        cmd->next = c->manage.cmd_queue;
 | 
						|
        cmd->prev = c->manage.cmd_queue->prev;
 | 
						|
        cmd->next->prev = cmd->prev->next = cmd;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        cmd->next = cmd->prev = cmd;
 | 
						|
        c->manage.cmd_queue = cmd;
 | 
						|
    }
 | 
						|
 | 
						|
    if (c->manage.cmd_queue == cmd)
 | 
						|
    {
 | 
						|
        *(command + size - 1) = '\n';
 | 
						|
        res = send(c->manage.sk, command, size, 0);
 | 
						|
        *(command + size - 1) = '\0';
 | 
						|
    }
 | 
						|
 | 
						|
    if (res != size)
 | 
						|
    {
 | 
						|
        if (res == SOCKET_ERROR)
 | 
						|
            res = 0;
 | 
						|
 | 
						|
        size -= res;
 | 
						|
        cmd->command = malloc(size);
 | 
						|
        if (cmd->command == NULL)
 | 
						|
        {
 | 
						|
            free(cmd);
 | 
						|
            return FALSE;
 | 
						|
        }
 | 
						|
        memcpy(cmd->command, command + res, size);
 | 
						|
        *(cmd->command + size - 1) = '\n';
 | 
						|
        cmd->size = size;
 | 
						|
    }
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Try to send a queued management command to OpenVPN
 | 
						|
 */
 | 
						|
static void
 | 
						|
SendCommand(connection_t *c)
 | 
						|
{
 | 
						|
    int res;
 | 
						|
    mgmt_cmd_t *cmd = c->manage.cmd_queue;
 | 
						|
    if (cmd == NULL || cmd->size == 0)
 | 
						|
        return;
 | 
						|
 | 
						|
    res = send(c->manage.sk, cmd->command, cmd->size, 0);
 | 
						|
    if (res < 1)
 | 
						|
        return;
 | 
						|
 | 
						|
    if (res != cmd->size)
 | 
						|
        memmove(cmd->command, cmd->command + res, cmd->size - res);
 | 
						|
 | 
						|
    cmd->size -= res;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Remove a command from a connection's command queue
 | 
						|
 */
 | 
						|
static BOOL
 | 
						|
UnqueueCommand(connection_t *c)
 | 
						|
{
 | 
						|
    mgmt_cmd_t *cmd = c->manage.cmd_queue;
 | 
						|
    if (!cmd)
 | 
						|
        return FALSE;
 | 
						|
 | 
						|
    if (cmd->type == combined)
 | 
						|
    {
 | 
						|
        cmd->type = regular;
 | 
						|
        return TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    if (cmd->next == cmd)
 | 
						|
    {
 | 
						|
        c->manage.cmd_queue = NULL;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        cmd->prev->next = cmd->next;
 | 
						|
        cmd->next->prev = cmd->prev;
 | 
						|
        c->manage.cmd_queue = cmd->next;
 | 
						|
        SendCommand(c);
 | 
						|
    }
 | 
						|
 | 
						|
    free(cmd->command);
 | 
						|
    free(cmd);
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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 = '\0';
 | 
						|
            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:", 9) == 0)
 | 
						|
            {
 | 
						|
                if (rtmsg_handler[password])
 | 
						|
                    rtmsg_handler[password](c, pos + 9);
 | 
						|
            }
 | 
						|
            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(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;
 | 
						|
    }
 | 
						|
}
 |