command[1] = 2;
int proxy_socks5_negotiate (Proxy_Socket p, int change){    if (p->state == PROXY_CHANGE_NEW) {	/* initial command:	 *  version number (1 byte) = 5	 *  number of available authentication methods (1 byte)	 *  available authentication methods (1 byte * previous value)	 *    authentication methods:	 *     0x00 = no authentication	 *     0x01 = GSSAPI	 *     0x02 = username/password	 *     0x03 = CHAP	 */	char command[4];	int len;	command[0] = 5; /* version 5 */	if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {	    command[1] = 2;	       /* two methods supported: */	    command[2] = 0x00;	       /* no authentication */	    command[3] = 0x02;	       /* username/password */	    len = 4;	} else {	    command[1] = 1;	       /* one methods supported: */	    command[2] = 0x00;	       /* no authentication */	    len = 3;	}	sk_write(p->sub_socket, command, len);	p->state = 1;	return 0;    }    if (change == PROXY_CHANGE_CLOSING) {	/* if our proxy negotiation process involves closing and opening	 * new sockets, then we would want to intercept this closing	 * callback when we were expecting it. if we aren't anticipating	 * a socket close, then some error must have occurred. we'll	 * just pass those errors up to the backend.	 */	return plug_closing(p->plug, p->closing_error_msg,			    p->closing_error_code,			    p->closing_calling_back);    }    if (change == PROXY_CHANGE_SENT) {	/* some (or all) of what we wrote to the proxy was sent.	 * we don't do anything new, however, until we receive the	 * proxy's response. we might want to set a timer so we can	 * timeout the proxy negotiation after a while...	 */	return 0;    }    if (change == PROXY_CHANGE_ACCEPTING) {	/* we should _never_ see this, as we are using our socket to	 * connect to a proxy, not accepting inbound connections.	 * what should we do? close the socket with an appropriate	 * error message?	 */	return plug_accepting(p->plug, p->accepting_sock);    }    if (change == PROXY_CHANGE_RECEIVE) {	/* we have received data from the underlying socket, which	 * we'll need to parse, process, and respond to appropriately.	 */	if (p->state == 1) {	    /* initial response:	     *  version number (1 byte) = 5	     *  authentication method (1 byte)	     *    authentication methods:	     *     0x00 = no authentication	     *     0x01 = GSSAPI	     *     0x02 = username/password 	     *     0x03 = CHAP	     *     0xff = no acceptable methods	     */	    char data[2];	    if (bufchain_size(&p->pending_input_data) < 2)		return 1;	       /* not got anything yet */	    /* get the response */	    bufchain_fetch(&p->pending_input_data, data, 2);	    if (data[0] != 5) {		plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version",			     PROXY_ERROR_GENERAL, 0);		return 1;	    }	    if (data[1] == 0x00) p->state = 2; /* no authentication needed */	    else if (data[1] == 0x01) p->state = 4; /* GSSAPI authentication */	    else if (data[1] == 0x02) p->state = 5; /* username/password authentication */	    else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */	    else {		plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication",			     PROXY_ERROR_GENERAL, 0);		return 1;	    }	    bufchain_consume(&p->pending_input_data, 2);	}	if (p->state == 7) {	    /* password authentication reply format:	     *  version number (1 bytes) = 1	     *  reply code (1 byte)	     *    0 = succeeded	     *    >0 = failed	     */	    char data[2];	    if (bufchain_size(&p->pending_input_data) < 2)		return 1;	       /* not got anything yet */	    /* get the response */	    bufchain_fetch(&p->pending_input_data, data, 2);	    if (data[0] != 1) {		plug_closing(p->plug, "Proxy error: SOCKS password "			     "subnegotiation contained wrong version number",			     PROXY_ERROR_GENERAL, 0);		return 1;	    }	    if (data[1] != 0) {		plug_closing(p->plug, "Proxy error: SOCKS proxy refused"			     " password authentication",			     PROXY_ERROR_GENERAL, 0);		return 1;	    }	    bufchain_consume(&p->pending_input_data, 2);	    p->state = 2;	       /* now proceed as authenticated */	}	if (p->state == 2) {	    /* request format:	     *  version number (1 byte) = 5	     *  command code (1 byte)	     *    1 = CONNECT	     *    2 = BIND	     *    3 = UDP ASSOCIATE	     *  reserved (1 byte) = 0x00	     *  address type (1 byte)	     *    1 = IPv4	     *    3 = domainname (first byte has length, no terminating null)	     *    4 = IPv6	     *  dest. address (variable)	     *  dest. port (2 bytes) [network order]	     */	    char command[512];	    int len;	    int type;	    type = sk_addrtype(p->remote_addr);	    if (type == ADDRTYPE_IPV4) {		len = 10;	       /* 4 hdr + 4 addr + 2 trailer */		command[3] = 1; /* IPv4 */		sk_addrcopy(p->remote_addr, command+4);	    } else if (type == ADDRTYPE_IPV6) {		len = 22;	       /* 4 hdr + 16 addr + 2 trailer */		command[3] = 4; /* IPv6 */		sk_addrcopy(p->remote_addr, command+4);	    } else {		assert(type == ADDRTYPE_NAME);		command[3] = 3;		sk_getaddr(p->remote_addr, command+5, 256);		command[4] = strlen(command+5);		len = 7 + command[4];  /* 4 hdr, 1 len, N addr, 2 trailer */	    }	    command[0] = 5; /* version 5 */	    command[1] = 1; /* CONNECT command */	    command[2] = 0x00;	    /* port */	    command[len-2] = (char) (p->remote_port >> 8) & 0xff;	    command[len-1] = (char) p->remote_port & 0xff;	    sk_write(p->sub_socket, command, len);	    p->state = 3;	    return 1;	}	if (p->state == 3) {	    /* reply format:	     *  version number (1 bytes) = 5	     *  reply code (1 byte)	     *    0 = succeeded	     *    1 = general SOCKS server failure	     *    2 = connection not allowed by ruleset	     *    3 = network unreachable	     *    4 = host unreachable	     *    5 = connection refused	     *    6 = TTL expired	     *    7 = command not supported	     *    8 = address type not supported	     * reserved (1 byte) = x00	     * address type (1 byte)	     *    1 = IPv4	     *    3 = domainname (first byte has length, no terminating null)	     *    4 = IPv6	     * server bound address (variable)	     * server bound port (2 bytes) [network order]	     */	    char data[5];	    int len;	    /* First 5 bytes of packet are enough to tell its length. */ 	    if (bufchain_size(&p->pending_input_data) < 5)		return 1;	       /* not got anything yet */	    /* get the response */	    bufchain_fetch(&p->pending_input_data, data, 5);	    if (data[0] != 5) {		plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number",			     PROXY_ERROR_GENERAL, 0);		return 1;	    }	    if (data[1] != 0) {		char buf[256];		strcpy(buf, "Proxy error: ");		switch (data[1]) {		  case 1: strcat(buf, "General SOCKS server failure"); break;		  case 2: strcat(buf, "Connection not allowed by ruleset"); break;		  case 3: strcat(buf, "Network unreachable"); break;		  case 4: strcat(buf, "Host unreachable"); break;		  case 5: strcat(buf, "Connection refused"); break;		  case 6: strcat(buf, "TTL expired"); break;		  case 7: strcat(buf, "Command not supported"); break;		  case 8: strcat(buf, "Address type not supported"); break;		  default: sprintf(buf+strlen(buf),				   "Unrecognised SOCKS error code %d",				   data[1]);		    break;		}		plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);		return 1;	    }	    /*	     * Eat the rest of the reply packet.	     */	    len = 6;		       /* first 4 bytes, last 2 */	    switch (data[3]) {	      case 1: len += 4; break; /* IPv4 address */	      case 4: len += 16; break;/* IPv6 address */	      case 3: len += (unsigned char)data[4]; break; /* domain name */	      default:		plug_closing(p->plug, "Proxy error: SOCKS proxy returned "			     "unrecognised address format",			     PROXY_ERROR_GENERAL, 0);		return 1;	    }	    if (bufchain_size(&p->pending_input_data) < len)		return 1;	       /* not got whole reply yet */	    bufchain_consume(&p->pending_input_data, len);	    /* we're done */	    proxy_activate(p);	    return 1;	}	if (p->state == 4) {	    /* TODO: Handle GSSAPI authentication */	    plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication",			 PROXY_ERROR_GENERAL, 0);	    return 1;	}	if (p->state == 5) {	    if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {		char userpwbuf[514];		int ulen, plen;		ulen = strlen(p->cfg.proxy_username);		if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;		plen = strlen(p->cfg.proxy_password);		if (plen > 255) plen = 255; if (plen < 1) plen = 1;		userpwbuf[0] = 1;      /* version number of subnegotiation */		userpwbuf[1] = ulen;		memcpy(userpwbuf+2, p->cfg.proxy_username, ulen);		userpwbuf[ulen+2] = plen;		memcpy(userpwbuf+ulen+3, p->cfg.proxy_password, plen);		sk_write(p->sub_socket, userpwbuf, ulen + plen + 3);		p->state = 7;	    } else 		plug_closing(p->plug, "Proxy error: Server chose "			     "username/password authentication but we "			     "didn't offer it!",			 PROXY_ERROR_GENERAL, 0);	    return 1;	}	if (p->state == 6) {	    /* TODO: Handle CHAP authentication */	    plug_closing(p->plug, "Proxy error: We don't support CHAP authentication",			 PROXY_ERROR_GENERAL, 0);	    return 1;	}    }    plug_closing(p->plug, "Proxy error: Unexpected proxy error",		 PROXY_ERROR_UNEXPECTED, 0);    return 1;}