XEphem/GUI/xephem/netmenu.c

1284 lines
35 KiB
C

/* code to manage networking */
#include <stdio.h>
#include <math.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/Separator.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
#include "xephem.h"
#define TOUT 180 /* max secs to wait for socket data.
* default http timeout is 3 minutes.
*/
static int net_save (void);
static void net_create_form (void);
static void defaultSOCKS (void);
static void net_setup (void);
static void ok_cb (Widget w, XtPointer client, XtPointer call);
static void tb_cb (Widget w, XtPointer client, XtPointer call);
static void cancel_cb (Widget w, XtPointer client, XtPointer call);
static void pw_cb (Widget w, XtPointer client, XtPointer call);
static void help_cb (Widget w, XtPointer client, XtPointer call);
static int tout (int maxt, int fd, int w);
static char *herr (char *errmsg);
static int connect_to (int sockfd, struct sockaddr *serv_addr, int addrlen);
static Widget netshell_w; /* the main form dialog */
static Widget ndir_w; /* net direct TB */
static Widget socks_w; /* SOCKS option TB */
static Widget socksh_w, socksp_w;/* SOCKS host and port TF */
static Widget proxy_w; /* Proxy option TB */
static Widget proxyh_w, proxyp_w;/* Proxy host and port TF */
static Widget auth_w; /* Auth option TB */
static Widget authn_w, authpw_w;/* Auth name and PW TF */
/* passed to the TB callbacks to choose network access method */
typedef enum {
NETDIRTB, NETPROXTB, NETSOCKSTB
} TB;
/* current info */
static int proxy_on; /* whether proxy network connection is on */
static char *proxy_host; /* proxy host */
static int proxy_port; /* proxy port */
static int socks_on; /* whether SOCKS network connection is on */
static char *socks_host; /* SOCKS host */
static char *socks_port; /* SOCKS port (as a string) */
static int auth_on; /* whether to add Auth info */
static char *auth_name; /* Auth name */
/* we keep the plaintext password here, but just display splats */
static char *auth_pw; /* malloced plain text password */
/* buffer for recvlineb() */
static char rb_linebuf[512]; /* [next .. bad-1] are good */
static int rb_next; /* index of next good char */
static int rb_unk; /* index of first unknown char */
static char netcategory[] = "Network"; /* Save category */
static const SSL_METHOD *ssl_method; /* global ssl dispatch structure for creating a ssl context */
static SSL_CTX *ssl_ctx; /* global ssl context structure for creating ssl connections */
/* initalize the OpenSSL library.
* return -1 and with excuse in msg[], else 0 if ok.
* N.B. is called implicit in httpsGET.
*/
int
init_ssl(char msg[])
{
if (!ssl_ctx) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
if (SSL_library_init() < 0) {
#else
if (!OPENSSL_init_ssl (0, NULL)) {
#endif
(void) sprintf (msg, "Could not initialize the OpenSSL library!");
return (-1);
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ssl_method = SSLv23_client_method();
#else
ssl_method = TLS_client_method();
#endif
ssl_ctx = SSL_CTX_new (ssl_method);
SSL_CTX_set_options (ssl_ctx, SSL_OP_NO_SSLv2);
}
return (0);
}
/* call to set up without actually bringing up the menus.
*/
void
net_create()
{
if (!netshell_w) {
net_create_form();
(void) net_save(); /* confirming here is just annoying */
}
}
void
net_manage()
{
net_create();
if (!isUp (netshell_w))
net_setup();
XtPopup (netshell_w, XtGrabNone);
set_something (netshell_w, XmNiconic, (XtArgVal)False);
}
/* Base64 stuff lifted from downloader */
static void
three_to_four(what, where)
unsigned char *what, *where;
{
static char Table64[64] = {
'A','B','C','D','E','F','G','H',
'I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X',
'Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n',
'o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3',
'4','5','6','7','8','9','+','/'
};
int i;
*where=(*what >> 2) & 63;
*(where+1)=((*what << 4) | (*(what+1) >> 4)) & 63;
*(where+2)=((*(what+1) << 2) | (*(what+2) >> 6)) & 63;
*(where+3)=*(what+2) & 63;
for (i=0;i<4;i++)
where[i]= Table64[where[i]];
}
static void
string_to_base64(plain, b64)
char *plain, *b64;
{
unsigned char four[4];
int len=strlen(plain),len2=0;
int i;
if (len%3) len2=(len/3 +1)*4 +1;
else len2=(len/3)*4 +1;
while (len>=3) {
three_to_four((unsigned char *)plain,four);
for (i=0;i<4;i++)
*(b64++)=four[i];
len-=3;
plain+=3;
}
if (len) {
unsigned char three[3]={0,0,0};
for (i=0;i<len;i++) {
three[i]=*((unsigned char*)plain);
plain++;
}
three_to_four(three,four);
for (i=3-(len==1);i<4;i++)
four[i]='=';
for (i=0;i<4;i++)
*(b64++)=four[i];
}
*b64=0;
}
/* add proxy-authorization to buf.
* N.B. we assume buf[] ends with \r\n
*/
static void
addAuth (buf)
char *buf;
{
char *nm = XmTextFieldGetString (authn_w);
char plainbuf[1024], b64buf[1024];
sprintf (plainbuf, "%s:%s", nm, auth_pw);
string_to_base64 (plainbuf, b64buf);
XtFree (nm);
/* insert before last \r\n */
sprintf (buf+strlen(buf)-2, "Proxy-Authorization: Basic %s\r\n\r\n",
b64buf);
}
/* open the host, do the given GET cmd, and return a socket fd for the result.
* return -1 and with excuse in msg[], else 0 if ok.
* N.B. can be called before we are created if net set in app defaults.
*/
int
httpGET (char *host, char *GETcmd, char msg[])
{
char buf[2048];
int fd;
int n;
/* open connection */
if (proxy_on) {
fd = mkconnection (proxy_host, proxy_port, msg);
if (fd < 0)
return (-1);
} else {
/* SOCKS or direct are both handled by mkconnection() */
fd = mkconnection (host, 80, msg);
if (fd < 0)
return (-1);
}
/* fill buf */
(void) sprintf (buf, "%s", GETcmd);
/* add proxy auth if enabled */
if (!auth_w)
net_create_form();
if (XmToggleButtonGetState (auth_w))
addAuth(buf);
/* log it */
xe_msg (0, "http: %s", buf);
/* send it */
n = strlen (buf);
if (sendbytes(fd, (unsigned char *)buf, n) < 0) {
(void) sprintf (msg, "%s: send error: %s", host, syserrstr());
(void) close (fd);
return (-1);
}
/* caller can read response */
return (fd);
}
/* establish a TCP connection to the named host on the given port.
* if ok return file descriptor, else -1 with excuse in msg[].
* try Socks if see socks_on.
* reset recvlineb() readahead in case old stuff left behind.
* N.B. we assume SIGPIPE is SIG_IGN
*/
int
mkconnection (
char *host, /* name of server */
int port, /* TCP port */
char msg[]) /* return diagnostic message here, if returning -1 */
{
struct sockaddr_in serv_addr;
struct hostent *hp;
int sockfd;
/* lookup host address.
* TODO: time out but even SIGALRM doesn't awaken this if it's stuck.
* I bet that's why netscape forks a separate dnshelper process!
*/
hp = gethostbyname (host);
if (!hp) {
(void) sprintf (msg, "Can not find IP of %s.\n%s", host,
herr ("Try entering IP directly"));
return (-1);
}
/* connect -- use socks if on */
if (socks_on) {
/* Connection over SOCKS server */
struct {
unsigned char VN; /* version number */
unsigned char CD; /* command code */
uint16_t DSTPORT; /* destination port */
uint32_t DSTIP; /* destination IP address */
} SocksPacket;
struct hostent *hs = gethostbyname (socks_host);
char *Socks_port_str = socks_port;
int Socks_port = Socks_port_str ? atoi (Socks_port_str) : 1080;
SocksPacket.VN = 4; /* version 4 */
SocksPacket.CD = 1; /* Command code 1 = connect request */
SocksPacket.DSTPORT = htons((short)port);
if (!hs) {
(void) sprintf (msg, "SOCKS: %s:\nCan not get server IP:\n%s.",
socks_host, herr("Try entering IP directly"));
return (-1);
}
(void) memset ((char *)&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr=
((struct in_addr *)(hs->h_addr_list[0]))->s_addr;
serv_addr.sin_port = htons((short)Socks_port);
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
(void) sprintf (msg, "SOCKS: %s/%d:\n%s", socks_host, port,
syserrstr());
return (-1);
}
/* Yes, again. Some variables inside are static */
hp = gethostbyname (host);
SocksPacket.DSTIP=((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
if (connect_to (sockfd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) {
(void) sprintf (msg, "SOCKS: %s: %s", socks_host, syserrstr());
(void) close(sockfd);
return (-1);
}
(void)write (sockfd, &SocksPacket, sizeof (SocksPacket));
(void)write (sockfd, "xephem", 7); /* yes, include trailing \0 */
(void)read (sockfd, &SocksPacket, sizeof (SocksPacket));
switch (SocksPacket.CD) {
case 90:
break; /* yes! */
case 92:
(void) sprintf (msg, "SOCKS: cannot connect to client");
(void) close(sockfd);
return (-1);
case 93:
(void) sprintf (msg, "SOCKS: client program and ident report different user-ids");
(void) close(sockfd);
return (-1);
default:
(void) sprintf (msg, "SOCKS: Request rejected or failed");
(void) close(sockfd);
return (-1);
}
} else {
/* normal connection without SOCKS server */
/* create a socket to the host's server */
(void) memset ((char *)&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr =
((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
serv_addr.sin_port = htons((short)port);
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
(void) sprintf (msg, "%s/%d: %s", host, port, syserrstr());
return (-1);
}
if (connect_to (sockfd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) {
(void) sprintf (msg, "%s: %s", host, syserrstr());
(void) close(sockfd);
return (-1);
}
}
/* reset readahead in case user uses recvlineb() */
rb_next = rb_unk = 0;
/* ok */
return (sockfd);
}
/* send n bytes from buf to socket fd.
* return 0 if ok else -1
*/
int
sendbytes (int fd, unsigned char buf[], int n)
{
int ns, tot;
for (tot = 0; tot < n; tot += ns) {
if (tout (TOUT, fd, 1) < 0)
return (-1);
ns = write (fd, (void *)(buf+tot), n-tot);
if (ns <= 0)
return (-1);
}
return (0);
}
/* receive exactly n bytes from socket fd into buf.
* return -1, 0 or n.
*/
int
recvbytes (int fd, unsigned char buf[], int n)
{
int ns, tot;
for (tot = 0; tot < n; tot += ns) {
if (tout (TOUT, fd, 0) < 0)
return (-1);
ns = read (fd, (void *)(buf+tot), n-tot);
if (ns <= 0)
return (ns);
}
return (n);
}
/* like read(2) except we time out and allow user to cancel.
* receive up to n bytes from socket fd into buf.
* return count, or 0 on eof or -1 on error.
*/
int
readbytes (int fd, unsigned char buf[], int n)
{
int ns;
if (tout (TOUT, fd, 0) < 0)
return (-1);
ns = read (fd, (void *)buf, n);
return (ns);
}
/* read up to and including the next '\n' from socket fd into buf[max].
* we silently ignore all '\r'. we add a trailing '\0'.
* return line length (not counting \0) if all ok, else -1.
* N.B. this never reads ahead -- if that's ok, recvlineb() is better
*/
int
recvline (int fd, char buf[], int max)
{
unsigned char c;
int n;
max--; /* leave room for trailing \0 */
for (n = 0; n < max && recvbytes (fd, &c, 1) == 1; ) {
if (c != '\r') {
buf[n++] = c;
if (c == '\n') {
buf[n] = '\0';
return (n);
}
}
}
return (-1);
}
/* rather like recvline but reads ahead in big chunk for efficiency.
* return length if read a line ok, 0 if hit eof, -1 if error.
* N.B. we silently swallow all '\r'.
* N.B. we read ahead and can hide bytes after each call.
*/
int
recvlineb (int sock, char *buf, int size)
{
char *origbuf = buf; /* save to prevent overfilling buf */
char c = '\0';
int ok = 1;
/* always leave room for trailing \n */
size -= 1;
/* read and copy linebuf[next] to buf until buf fills or copied a \n */
do {
if (rb_next >= rb_unk) {
/* linebuf is empty -- refill */
int nr;
if (tout (TOUT, sock, 0) < 0) {
nr = -1;
break;
}
nr = read (sock, rb_linebuf, sizeof(rb_linebuf));
if (nr <= 0) {
ok = nr;
rb_next = 0;
rb_unk = 0;
break;
}
rb_next = 0;
rb_unk = nr;
}
if ((c = rb_linebuf[rb_next++]) != '\r')
*buf++ = c;
} while (buf-origbuf < size && c != '\n');
/* always give back a real line regardless, else status */
if (ok > 0) {
*buf = '\0';
ok = buf - origbuf;
}
return (ok);
}
/* open the host, do the given GET cmd, and return a socket fd for the result.
* on success it fills the XE_SSL_FD structure for later use by SSL_read() and necessary cleanup.
* return -1 and with excuse in msg[], else 0 if ok.
* N.B. can be called before we are created if net set in app defaults.
*/
int
httpsGET (char *host, char *GETcmd, char msg[], XE_SSL_FD *ssl_fd)
{
char buf[2048];
int fd;
int connected;
SSL *ssl;
int n;
int ret;
int httpsport = 443;
/* initialize the ssl library */
if (init_ssl (msg) < 0) {
return (-1);
}
/* open connection */
if (proxy_on) {
fd = mkconnection (proxy_host, proxy_port, msg);
if (fd < 0)
return (-1);
/* fill buf with CONNECT */
(void) sprintf (buf, "CONNECT %1$s:%2$d HTTP/1.0\r\nUser-Agent: xephem/%3$s\r\nHost: %1$s:%2$d\r\n\r\n", host, httpsport, PATCHLEVEL);
/* add proxy auth if enabled */
if (!auth_w)
net_create_form();
if (XmToggleButtonGetState (auth_w))
addAuth(buf);
/* log it */
xe_msg (0, "https proxy connect: %s", buf);
/* send it */
n = strlen (buf);
if (sendbytes(fd, (unsigned char *)buf, n) < 0) {
(void) sprintf (msg, "%s: send error: %s", proxy_host, syserrstr());
(void) close (fd);
return (-1);
}
connected = 0;
while (recvline (fd, buf, sizeof(buf)) > 1) {
xe_msg (0, "Rcv: %s", buf);
if (strstr (buf, "200 "))
connected = 1;
}
if (!connected) {
(void) sprintf (msg, "%s: connect error: %s", proxy_host, syserrstr());
(void) close (fd);
return (-1);
}
} else {
/* SOCKS or direct are both handled by mkconnection() */
fd = mkconnection (host, httpsport, msg);
if (fd < 0)
return (-1);
}
/* fill buf with GETcmd */
(void) sprintf (buf, "%s", GETcmd);
/* start ssl connection */
ssl = SSL_new (ssl_ctx);
SSL_set_fd (ssl, fd);
SSL_connect (ssl);
/* log it */
xe_msg (0, "https: %s", buf);
/* send it */
n = strlen (buf);
ret = SSL_write (ssl, (unsigned char *)buf, n);
if (ret <= 0) {
(void) sprintf (msg, "%s: ssl send error code: %d", host, SSL_get_error (ssl, ret));
(void) SSL_free (ssl);
(void) close (fd);
return (-1);
}
/* caller can read response */
ssl_fd->fd = fd;
ssl_fd->ssl = ssl;
return (fd);
}
/* receive exactly n bytes from ssl connection ssl_fd into buf.
* return -1, 0 or n.
* N.B. with fallback to ordinary read from socket if ssl_fd->ssl is NULL
*/
int
ssl_recvbytes (XE_SSL_FD *ssl_fd, unsigned char buf[], int n)
{
int ns, tot;
for (tot = 0; tot < n; tot += ns) {
if (tout (TOUT, ssl_fd->fd, 0) < 0)
return (-1);
if (ssl_fd->ssl)
ns = SSL_read (ssl_fd->ssl, (void *)(buf+tot), n-tot);
else
ns = read (ssl_fd->fd, (void *)(buf+tot), n-tot);
if (ns <= 0)
return (ns);
}
return (n);
}
/* like read(2) except we time out and allow user to cancel.
* receive up to n bytes from ssl connection ssl_fd into buf.
* return count, or 0 on eof or -1 on error.
* N.B. with fallback to ordinary read from socket if ssl_fd->ssl is NULL
*/
int
ssl_readbytes (XE_SSL_FD *ssl_fd, unsigned char buf[], int n)
{
int ns;
if (tout (TOUT, ssl_fd->fd, 0) < 0)
return (-1);
if (ssl_fd->ssl)
ns = SSL_read (ssl_fd->ssl, (void *)buf, n);
else
ns = read (ssl_fd->fd, (void *)buf, n);
return (ns);
}
/* read up to and including the next '\n' from ssl into buf[max].
* we silently ignore all '\r'. we add a trailing '\0'.
* return line length (not counting \0) if all ok, else -1.
* N.B. with fallback to ordinary read from socket if ssl_fd->ssl is NULL
*/
int
ssl_recvline (XE_SSL_FD *ssl_fd, char buf[], int max)
{
unsigned char c;
int n;
max--; /* leave room for trailing \0 */
for (n = 0; n < max && ssl_recvbytes (ssl_fd, &c, 1) == 1; ) {
if (c != '\r') {
buf[n++] = c;
if (c == '\n') {
buf[n] = '\0';
return (n);
}
}
}
return (-1);
}
/* rather like ssl_recvline but reads ahead in big chunk for efficiency.
* return length if read a line ok, 0 if hit eof, -1 if error.
* N.B. we silently swallow all '\r'.
* N.B. we read ahead and can hide bytes after each call.
* N.B. with fallback to ordinary read from socket if ssl_fd->ssl is NULL
*/
int
ssl_recvlineb (XE_SSL_FD *ssl_fd, char *buf, int size)
{
char *origbuf = buf; /* save to prevent overfilling buf */
char c = '\0';
int ok = 1;
/* always leave room for trailing \n */
size -= 1;
/* read and copy linebuf[next] to buf until buf fills or copied a \n */
do {
if (rb_next >= rb_unk) {
/* linebuf is empty -- refill */
int nr;
if (tout (TOUT, ssl_fd->fd, 0) < 0) {
nr = -1;
break;
}
if (ssl_fd->ssl)
nr = SSL_read (ssl_fd->ssl, rb_linebuf, sizeof(rb_linebuf));
else
nr = read (ssl_fd->fd, rb_linebuf, sizeof(rb_linebuf));
if (nr <= 0) {
ok = nr;
rb_next = 0;
rb_unk = 0;
break;
}
rb_next = 0;
rb_unk = nr;
}
if ((c = rb_linebuf[rb_next++]) != '\r')
*buf++ = c;
} while (buf-origbuf < size && c != '\n');
/* always give back a real line regardless, else status */
if (ok > 0) {
*buf = '\0';
ok = buf - origbuf;
}
return (ok);
}
static void
net_create_form()
{
Widget netform_w;
Widget f, w;
Arg args[20];
int n;
/* create form */
n = 0;
XtSetArg (args[n], XmNcolormap, xe_cm); n++;
XtSetArg (args[n], XmNtitle,"xephem Network setup");n++;
XtSetArg (args[n], XmNiconName, "Net"); n++;
XtSetArg (args[n], XmNdeleteResponse, XmUNMAP); n++;
netshell_w = XtCreatePopupShell ("NetSetup", topLevelShellWidgetClass,
toplevel_w, args, n);
setup_icon (netshell_w);
set_something (netshell_w, XmNcolormap, (XtArgVal)xe_cm);
sr_reg (netshell_w, "XEphem*NetSetup.x", netcategory, 0);
sr_reg (netshell_w, "XEphem*NetSetup.y", netcategory, 0);
n = 0;
XtSetArg (args[n], XmNmarginHeight, 10); n++;
XtSetArg (args[n], XmNmarginWidth, 10); n++;
XtSetArg (args[n], XmNverticalSpacing, 10); n++;
netform_w = XmCreateForm (netshell_w, "NetForm", args, n);
XtAddCallback (netform_w, XmNhelpCallback, help_cb, NULL);
XtManageChild (netform_w);
/* make the title */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
w = XmCreateLabel (netform_w, "NetT", args, n);
set_xmstring (w, XmNlabelString, "Network setup:");
XtManageChild (w);
/* make the Direct toggle */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNindicatorType, XmONE_OF_MANY); n++;
ndir_w = XmCreateToggleButton (netform_w, "Direct", args, n);
XtAddCallback (ndir_w, XmNvalueChangedCallback, tb_cb,
(XtPointer)NETDIRTB);
set_xmstring (ndir_w, XmNlabelString, "Direct connect");
wtip (ndir_w, "Use direct internet connection (no proxy or SOCKS)");
XtManageChild (ndir_w);
sr_reg (ndir_w, NULL, netcategory, 1);
/* make the SOCKS toggle and info (first because label is wider) */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, ndir_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNindicatorType, XmONE_OF_MANY); n++;
socks_w = XmCreateToggleButton (netform_w, "SOCKS", args, n);
set_xmstring (socks_w, XmNlabelString, "via SOCKS");
XtAddCallback (socks_w, XmNvalueChangedCallback, tb_cb,
(XtPointer)NETSOCKSTB);
wtip (socks_w, "Use SOCKS V4 to Internet");
XtManageChild (socks_w);
sr_reg (socks_w, NULL, netcategory, 1);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, ndir_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNleftWidget, socks_w); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
XtSetArg (args[n], XmNcolumns, 5); n++;
socksp_w = XmCreateTextField (netform_w, "SOCKSPort", args, n);
wtip (socksp_w, "SOCKS port number");
XtManageChild (socksp_w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, ndir_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNleftWidget, socksp_w); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNcolumns, 40); n++;
socksh_w = XmCreateTextField (netform_w, "SOCKSHost", args, n);
wtip (socksh_w, "Name of SOCKS host");
XtManageChild (socksh_w);
defaultSOCKS();
sr_reg (socksp_w, NULL, netcategory, 1);
sr_reg (socksh_w, NULL, netcategory, 1);
/* make the Proxy toggle and info */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, socksh_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNindicatorType, XmONE_OF_MANY); n++;
proxy_w = XmCreateToggleButton (netform_w, "Proxy", args, n);
set_xmstring (proxy_w, XmNlabelString, "via Proxy");
XtAddCallback (proxy_w, XmNvalueChangedCallback, tb_cb,
(XtPointer)NETPROXTB);
wtip (proxy_w, "Reach the Internet through a proxy");
XtManageChild (proxy_w);
sr_reg (proxy_w, NULL, netcategory, 1);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, socksh_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNleftWidget, socks_w); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
XtSetArg (args[n], XmNcolumns, 5); n++;
proxyp_w = XmCreateTextField (netform_w, "ProxyPort", args, n);
wtip (proxyp_w, "Proxy port number");
XtManageChild (proxyp_w);
sr_reg (proxyp_w, NULL, netcategory, 1);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, socksh_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNleftWidget, proxyp_w); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNcolumns, 40); n++;
proxyh_w = XmCreateTextField (netform_w, "ProxyHost", args, n);
wtip (proxyh_w, "Name of Proxy host");
XtManageChild (proxyh_w);
sr_reg (proxyh_w, NULL, netcategory, 1);
/* make the Auth option and info */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, proxyh_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNindicatorType, XmN_OF_MANY); n++;
auth_w = XmCreateToggleButton (netform_w, "Auth", args, n);
set_xmstring (auth_w, XmNlabelString, "Auth Name");
wtip (auth_w, "Apply authentication name and password");
XtManageChild (auth_w);
sr_reg (auth_w, NULL, netcategory, 1);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, proxyh_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNleftWidget, auth_w); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
XtSetArg (args[n], XmNcolumns, 15); n++;
authn_w = XmCreateTextField (netform_w, "AuthName", args, n);
wtip (authn_w, "Authentication name");
XtManageChild (authn_w);
sr_reg (authn_w, NULL, netcategory, 1);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, proxyh_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNleftWidget, authn_w); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
w = XmCreateLabel (netform_w, "Password", args, n);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, proxyh_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNleftWidget, w); n++;
XtSetArg (args[n], XmNleftOffset, 10); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNcolumns, 15); n++;
authpw_w = XmCreateTextField (netform_w, "AuthPassword", args, n);
XtAddCallback (authpw_w, XmNmodifyVerifyCallback, pw_cb, 0);
wtip (authpw_w, "Authentication password");
XtManageChild (authpw_w);
/* malloc null string to start */
auth_pw = XtNewString ("");
/* make the controls across the bottom under a separator */
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, authpw_w); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
w = XmCreateSeparator (netform_w, "Sep", args, n);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg (args[n], XmNtopWidget, w); n++;
XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg (args[n], XmNfractionBase, 21); n++;
f = XmCreateForm (netform_w, "CF", args, n);
XtManageChild (f);
n = 0;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 3); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 6); n++;
w = XmCreatePushButton (f, "Ok", args, n);
wtip (w, "Install changes and close dialog");
XtAddCallback (w, XmNactivateCallback, ok_cb, NULL);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 9); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 12); n++;
w = XmCreatePushButton (f, "Close", args, n);
wtip (w, "Close this menu without doing anything");
XtAddCallback (w, XmNactivateCallback, cancel_cb, NULL);
XtManageChild (w);
n = 0;
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNleftPosition, 15); n++;
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
XtSetArg (args[n], XmNrightPosition, 18); n++;
w = XmCreatePushButton (f, "Help", args, n);
wtip (w, "More detailed descriptions");
XtAddCallback (w, XmNactivateCallback, help_cb, NULL);
XtManageChild (w);
}
/* init SOCKS host and port. first check SOCKS_PORT and SOCKS_NS env variables,
* respectively (same ones used by netscape) then X resources.
*/
static void
defaultSOCKS()
{
char *str;
str = getenv ("SOCKS_PORT");
if (str)
XmTextFieldSetString (socksp_w, str ? str : "1080");
str = getenv ("SOCKS_NS");
if (str)
XmTextFieldSetString (socksh_w, str);
}
/* set up the dialog according to our static state */
static void
net_setup ()
{
/* Net */
XmToggleButtonSetState (ndir_w, !socks_on && !proxy_on, False);
XmToggleButtonSetState (proxy_w, proxy_on, False);
if (proxy_host)
XmTextFieldSetString (proxyh_w, proxy_host);
if (proxy_port) {
char buf[32];
(void) sprintf (buf, "%d", proxy_port);
XmTextFieldSetString (proxyp_w, buf);
}
XmToggleButtonSetState (socks_w, socks_on, False);
if (socks_host)
XmTextFieldSetString (socksh_w, socks_host);
if (socks_port)
XmTextFieldSetString (socksp_w, socks_port);
XmToggleButtonSetState (auth_w, auth_on, False);
if (auth_name)
XmTextFieldSetString (authn_w, auth_name);
}
/* save the dialog as our static state.
* if any major trouble, issue xe_msg and return -1, else return 0.
*/
static int
net_save ()
{
char *str, msg[1024];
int allok = 1;
int fd;
watch_cursor (1);
/* Network setup.
* N.B. do this before using the network :-)
*/
proxy_on = XmToggleButtonGetState (proxy_w);
if (proxy_host)
XtFree (proxy_host);
proxy_host = XmTextFieldGetString (proxyh_w);
str = XmTextFieldGetString (proxyp_w);
proxy_port = atoi (str);
XtFree (str);
if (proxy_on) {
fd = mkconnection (proxy_host, proxy_port, msg);
if (fd < 0) {
xe_msg (1, "%s", msg);
proxy_on = 0;
net_setup ();
allok = 0;
} else
(void) close (fd);
}
socks_on = XmToggleButtonGetState (socks_w);
if (socks_host)
XtFree (socks_host);
socks_host = XmTextFieldGetString (socksh_w);
if (socks_port)
XtFree (socks_port);
socks_port = XmTextFieldGetString (socksp_w);
if (socks_on) {
/* TODO: how to test? */
fd = mkconnection (socks_host, atoi(socks_port), msg);
if (fd < 0) {
xe_msg (1, "%s", msg);
socks_on = 0;
net_setup ();
allok = 0;
} else
(void) close (fd);
}
auth_on = XmToggleButtonGetState (auth_w);
if (auth_name)
XtFree (auth_name);
auth_name = XmTextFieldGetString (authn_w);
/* TODO: how to test? */
watch_cursor (0);
return (allok ? 0 : -1);
}
/* called from Ok */
/* ARGSUSED */
static void
ok_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
if (net_save() == 0)
XtPopdown (netshell_w);
}
/* called from Ok */
/* ARGSUSED */
static void
cancel_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
/* outta here */
XtPopdown (netshell_w);
}
/* called whenever the Password text field is edited.
* add to auth_pw but echo as *
*/
/* ARGSUSED */
static void
pw_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
XmTextVerifyCallbackStruct *vp = (XmTextVerifyCallbackStruct *)call;
int l = XmTextFieldGetLastPosition(w);
int sp = vp->startPos;
int ep = vp->endPos;
if (sp < ep) {
/* cut sp .. ep-1 */
memmove (auth_pw+sp, auth_pw+ep, l-ep+1); /* EOS too */
l -= ep-sp;
}
if (vp->text->length > 0) {
/* insert 1 @ sp */
auth_pw = XtRealloc (auth_pw, l+2); /* new + EOS */
memmove (auth_pw+sp+1, auth_pw+sp, l-sp+1); /* EOS too */
auth_pw[sp] = vp->text->ptr[0];
/* echo as '*' */
vp->text->ptr[0] = '*';
}
}
/* called from any of the choice toggle buttons.
* client is one of the TB enums to tell us which.
*/
/* ARGSUSED */
static void
tb_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
if (XmToggleButtonGetState(w)) {
switch ((long int)client) {
case NETDIRTB:
/* turn off proxy, socks and auth */
XmToggleButtonSetState (proxy_w, False, False);
XmToggleButtonSetState (socks_w, False, False);
XmToggleButtonSetState (auth_w, False, False);
break;
case NETPROXTB:
/* turn off direct and socks */
XmToggleButtonSetState (ndir_w, False, False);
XmToggleButtonSetState (socks_w, False, False);
break;
case NETSOCKSTB:
/* turn off direct and proxy */
XmToggleButtonSetState (ndir_w, False, False);
XmToggleButtonSetState (proxy_w, False, False);
break;
default:
printf ("FS: bad client: %d\n", (int)(long int)client);
abort();
}
}
}
/* called from Ok */
/* ARGSUSED */
static void
help_cb (w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
static char *msg[] = {"Set up network connectivity options."};
hlp_dialog ("NetSetup", msg, sizeof(msg)/sizeof(msg[0]));
}
/* wait at most maxt secs for the ability to read/write using fd and allow X
* processing in the mean time.
* w is 0 is for reading, 1 for writing, 2 for either.
* return 0 if ok to proceed, else -1 if trouble or timeout.
*/
static int
tout (maxt, fd, w)
int maxt;
int fd;
int w;
{
int i;
for (i = 0; stopd_check() == 0 && i < maxt; i++) {
fd_set rset, wset;
struct timeval tv;
int ret;
FD_ZERO (&rset);
FD_ZERO (&wset);
switch (w) {
case 0:
FD_SET (fd, &rset);
break;
case 1:
FD_SET (fd, &wset);
break;
case 2:
FD_SET (fd, &rset);
FD_SET (fd, &wset);
break;
default:
printf ("Bug: tout() called with %d\n", w);
abort();
}
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = select (fd+1, &rset, &wset, NULL, &tv);
if (ret > 0)
return (0);
if (ret < 0)
return (-1);
}
errno = i == maxt ? ETIMEDOUT : EINTR;
return (-1);
}
/* a networking error has occured. if we can dig out more details about why
* using h_errno, return its message, otherwise just return errmsg unchanged.
* we do this because we don't know how portable is h_errno?
*/
static char *
herr (errmsg)
char *errmsg;
{
#if defined(HOST_NOT_FOUND) && defined(TRY_AGAIN)
switch (h_errno) {
case HOST_NOT_FOUND:
errmsg = "Host Not Found";
break;
case TRY_AGAIN:
errmsg = "Might be a temporary condition -- try again later";
break;
}
#endif
return (errmsg);
}
/* just like connect(2) but tries to time out after TOUT yet let X continue.
* return 0 if ok, else -1.
*/
static int
connect_to (sockfd, serv_addr, addrlen)
int sockfd;
struct sockaddr *serv_addr;
int addrlen;
{
#ifdef O_NONBLOCK /* _POSIX_SOURCE */
#define NOBLOCK O_NONBLOCK
#else
#define NOBLOCK O_NDELAY
#endif
unsigned int len;
int err;
int flags;
int ret;
/* set socket non-blocking */
flags = fcntl (sockfd, F_GETFL, 0);
(void) fcntl (sockfd, F_SETFL, flags | NOBLOCK);
/* start the connect */
ret = connect (sockfd, serv_addr, addrlen);
if (ret < 0 && errno != EINPROGRESS)
return (-1);
/* wait for sockfd to become useable */
ret = tout (TOUT, sockfd, 2);
if (ret < 0)
return (-1);
/* verify connection really completed */
len = sizeof(err);
err = 0;
ret = getsockopt (sockfd, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
if (ret < 0)
return (-1);
if (err != 0) {
errno = err;
return (-1);
}
/* looks good - restore blocking */
(void) fcntl (sockfd, F_SETFL, flags);
return (0);
}