/* Send data to the server, checking and reporting errors */ int _pg_send(void *data,u32 datasize) { if (send(_pgsockfd,data,datasize,0)!=datasize) { clienterr("send error"); return 1; } return 0; }
/* Malloc wrapper */ void *_pg_malloc(size_t size) { void *p; p = malloc(size); if (!p) clienterr("out of memory"); return p; }
/* Receive... */ int _pg_recv(void *data,u32 datasize) { while (1) { if (recv(_pgsockfd,data,datasize,0) >= 0) break; /* if we have been interrupted (by a signal for instance), restart the reception */ if (errno != EINTR) { clienterr("recv error"); return 1; } } return 0; }
/* Some 'user friendly' default sig handlers */ void _pgsig(int sig) { char *a,*b; u32 id; switch (sig) { case SIGSEGV: id = PGRES_STRING_SEGFAULT; break; case SIGFPE: id = PGRES_STRING_MATHERR; break; default: return; } a = pgGetString(pgGetServerRes(id)); b = alloca(strlen(a)+1); strcpy(b,a); clienterr(b); }
/* Open a connection to the server, parsing PicoGUI commandline options if they are present */ void pgInit(int argc, char **argv) { int numbytes; struct pghello ServerInfo; #ifndef CONFIG_UNIX_SOCKET struct hostent *he; struct sockaddr_in server_addr; /* connector's address information */ #else struct sockaddr_un server_addr; #endif const char *hostname; u16 port = PG_REQUEST_PORT; const char *appletparam = NULL; int fd,i,j,args_to_shift; char *arg; volatile int tmp; #ifdef UCLINUX struct in_addr srv_addr; #endif struct stat st; int enable_warning = 1; if (_pgsockfd) { printf("PicoGUI cli_c: Warning, pgInit called multiple times\n"); return; } /* Save the program's name */ if (argc > 0) _pg_appname = argv[0]; /* Set default handlers */ pgSetErrorHandler(&_pg_defaulterr); _pgselect_handler = &select; _pgselect_bottomhalf = NULL; signal(SIGSEGV,&_pgsig); signal(SIGFPE,&_pgsig); /* Default tunables */ hostname = getenv("PGSERVER"); if ((!hostname) || (!*hostname)) hostname = PG_REQUEST_SERVER; /* Handle arguments we recognize, Leave others for the app */ if (argc > 0) { for (i=1;i<argc;i++) { arg = argv[i]; /* It's ours if it starts with --pg */ if (!bcmp(arg,"--pg",4)) { arg+=4; args_to_shift = 1; if (!strcmp(arg,"server")) { /* --pgserver : Next argument is the picogui server */ args_to_shift = 2; hostname = argv[i+1]; setenv("pgserver",hostname,1); /* Child processes inherit server */ } else if (!strcmp(arg,"version")) { /* --pgversion : For now print CVS id */ fprintf(stderr,"$Id$\n"); exit(1); } else if (!strcmp(arg,"applet")) { /* --pgapplet : Create the app in a public container instead of * registering a new app. */ args_to_shift = 2; appletparam = argv[i+1]; } else if (!strcmp(arg,"nowarn")) { enable_warning = 0; } else { /* Other command, print some help */ fprintf(stderr,"PicoGUI Client Library\nCommands: --pgserver --pgversion --pgapplet --pgnowarn\n"); exit(1); } /* Remove this argument - shuffle all future args back a space */ argc -= args_to_shift; for (j=i;j<argc;j++) argv[j] = argv[j+args_to_shift]; i -= args_to_shift; } } /* Some programs might rely on this? */ argv[argc] = NULL; } /* Separate the display number from the hostname */ arg = strrchr(hostname,':'); if (arg) { port = PG_REQUEST_PORT + atoi(arg+1); *arg = 0; } /* Only a display? Use default server */ if (!*hostname) hostname = PG_REQUEST_SERVER; #ifndef CONFIG_UNIX_SOCKET # ifdef UCLINUX /* get the host info. * gethostbyname() and gethostbyaddr() not working in uClinux. * Using inet_aton() * I let the two first in the case of... or for the future. */ if ((he=gethostbyname(hostname)) != NULL) { srv_addr = *((struct in_addr *)he->h_addr); } else if ((he=gethostbyaddr(hostname,strlen(hostname),AF_INET)) != NULL) { srv_addr = *((struct in_addr *)he->h_addr); } else if (inet_aton(hostname, &srv_addr)) { } else { clienterr("Error resolving server hostname"); return; } # else if ((he=gethostbyname(hostname)) == NULL) { /* get the host info */ clienterr("Error resolving server hostname"); return; } # endif #endif #ifndef CONFIG_UNIX_SOCKET if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { #else if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { #endif clienterr("socket error"); return; } #ifndef CONFIG_UNIX_SOCKET /* Try disabling the "Nagle algorithm" or "tinygram prevention" */ tmp = 1; setsockopt(fd,6 /*PROTO_TCP*/,TCP_NODELAY,(void *)&tmp,sizeof(tmp)); server_addr.sin_family = AF_INET; /* host byte order */ server_addr.sin_port = htons(port); /* short, network byte order */ #ifdef UCLINUX server_addr.sin_addr = srv_addr; #else server_addr.sin_addr = *((struct in_addr *)he->h_addr); #endif bzero(&(server_addr.sin_zero), 8); /* zero the rest of the struct */ if (connect(fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { #else server_addr.sun_family = AF_UNIX; strcpy(server_addr.sun_path,hostname); i = strlen(server_addr.sun_path) + sizeof(server_addr.sun_family); if (connect(fd, (struct sockaddr *)&server_addr, i) == -1) { #endif perror ("connect"); clienterr("Error connecting to server"); return; } /* We're connected */ _pgsockfd = fd; /* Don't let child processes inherit the connection */ fcntl(fd,F_SETFD,fcntl(fd,F_GETFD,0) | FD_CLOEXEC); /* Receive the hello packet and convert byte order */ if (_pg_recv(&ServerInfo,sizeof(ServerInfo))) return; ServerInfo.magic = ntohl(ServerInfo.magic); ServerInfo.protover = ntohs(ServerInfo.protover); /* Validate it */ if(ServerInfo.magic != PG_REQUEST_MAGIC) { clienterr("server has bad magic number"); return; } if((ServerInfo.protover < PG_PROTOCOL_VER) && enable_warning) { const char *s1, *s2; char * copys1; /* Since we know there's a compatibility problem now, it's unsafe * to create a message dialog. We'll try it anyway, but first * print a message to the console. */ printf("*********** PicoGUI incompatibility ************\n" "Server is version %d, require at least version %d\n", ServerInfo.protover, PG_PROTOCOL_VER); /* We must copy the first string temporarily because the pgGetString * buffer is only valid until the next picogui call */ s1 = pgGetString(pgGetServerRes(PGRES_STRING_PGUIWARN)); copys1 = alloca(strlen(s1)+1); strcpy(copys1, s1); s2 = pgGetString(pgGetServerRes(PGRES_STRING_PGUICOMPAT)), pgMessageDialog(copys1,s2,0); } /* Set up applet handle */ if (appletparam) { /* Is it a number or widget name? */ if (isdigit(*appletparam)) _pg_appletbox = atol(appletparam); else _pg_appletbox = pgFindWidget(appletparam); } } void pgSetErrorHandler(void (*handler)(u16 errortype, const char *msg)) { _pgerrhandler = handler; } const char *pgErrortypeString(u16 errortype) { switch (errortype) { case PG_ERRT_MEMORY: return "MEMORY"; case PG_ERRT_IO: return "IO"; case PG_ERRT_NETWORK: return "NETWORK"; case PG_ERRT_BADPARAM: return "BADPARAM"; case PG_ERRT_HANDLE: return "HANDLE"; case PG_ERRT_INTERNAL: return "INTERNAL"; case PG_ERRT_BUSY: return "BUSY"; case PG_ERRT_CLIENT: return "CLIENT"; case PG_ERRT_NONE: return "NONE"; case PG_ERRT_FILEFMT: return "FILEFMT"; } return "UNKNOWN"; } /* Sets an idle handler using nonblocking IO. See details in client_c.h */ pgidlehandler pgSetIdle(s32 t,pgidlehandler handler) { pgidlehandler previous = _pgidle_handler; if (!handler) t = 0; if (!t) handler = 0; _pgidle_handler = handler; _pgidle_period.tv_sec = t / 1000; _pgidle_period.tv_usec = (t % 1000) * 1000; return previous; } /* Flushes the buffer of packets - if there's only one, it gets sent as is More than one packet is wrapped in a batch packet. This checks for errors. If a return value is needed, the API function will call FlushRequests directly. If the client calls this it is assumed that they don't care about the return value (anything needing a return value would have called it explicitly) Return data is stored in _pg_return (yes, it's a little messy, but it's more efficient than a static variable in the function that must be passed out as a pointer) I believe in niceness for APIs and efficiency for the guts 8-) */ void pgFlushRequests(void) { #ifdef DEBUG printf("Flushed %d packet(s), %d bytes\n",_pgreqbuffer_count,_pgreqbuffer_size); #endif /* Free the data! */ if (_pg_return.type == PG_RESPONSE_DATA && _pg_return.e.data.data) { free(_pg_return.e.data.data); _pg_return.e.data.data = NULL; } if (_pg_return.type == PG_RESPONSE_EVENT && (_pg_return.e.event.type & PG_EVENTCODINGMASK) == PG_EVENTCODING_DATA && _pg_return.e.event.e.data.pointer) { free(_pg_return.e.event.e.data.pointer); _pg_return.e.event.e.data.pointer = NULL; } if (!_pgreqbuffer_count) { /* No packets */ return; } else if (_pgreqbuffer_count==1) { /* One packet- send as is */ if (_pg_send(_pgreqbuffer,_pgreqbuffer_size)) return; } else { /* Many packets - use a batch packet */ struct pgrequest batch_header; batch_header.type = htons(PGREQ_BATCH); batch_header.id = ++_pgrequestid; /* Order doesn't matter for id */ batch_header.size = htonl(_pgreqbuffer_size); if (_pg_send(&batch_header,sizeof(batch_header))) return; if (_pg_send(_pgreqbuffer,_pgreqbuffer_size)) return; } /* Reset buffer */ _pgreqbuffer_size = _pgreqbuffer_count = 0; /* Need a response packet back... If the last packet added to the buffer was a 'wait', * flag _pg_getresponse that it's ok to run idle handlers and select handlers. */ _pg_getresponse(_pgreqbuffer_lasttype == PGREQ_WAIT); }
void _pg_getresponse(int eventwait) { s16 rsp_id = 0; int extra_packet = 0; /* Read the response type. This is where the client spends almost * all it's time waiting (and the only safe place to interrupt) * so handle the idling here. */ if ( eventwait && ( ((_pgidle_period.tv_sec + _pgidle_period.tv_usec)&&!_pgidle_lock) || (_pgselect_handler != &select) ) ) { /* Use the interruptable wait (only for event waits!) */ switch (_pg_recvtimeout(&_pg_return.type)) { case 2: /* There's an extra packet after this one (return code from 'ping') * so we'll need to suck that up after we're done processing the * actual event packet. */ extra_packet = 1; break; case 0: break; default: return; } } else { /* Normal receive */ if (_pg_recv(&_pg_return.type,sizeof(_pg_return.type))) return; } _pg_return.type = ntohs(_pg_return.type); switch (_pg_return.type) { case PG_RESPONSE_ERR: { /* Error */ struct pgresponse_err pg_err; char *msg; /* Read the rest of the error (already have response type) */ pg_err.type = _pg_return.type; if (_pg_recv(((char*)&pg_err)+sizeof(_pg_return.type), sizeof(pg_err)-sizeof(_pg_return.type))) return; rsp_id = pg_err.id; pg_err.errt = ntohs(pg_err.errt); pg_err.msglen = ntohs(pg_err.msglen); /* Dynamically allocated buffer for the error message */ msg = alloca(pg_err.msglen+1); if(_pg_recv(msg,pg_err.msglen)) return; msg[pg_err.msglen] = 0; (*_pgerrhandler)(pg_err.errt,msg); } break; case PG_RESPONSE_RET: { /* Return value */ struct pgresponse_ret pg_ret; /* Read the rest of the error (already have response type) */ pg_ret.type = _pg_return.type; if (_pg_recv(((char*)&pg_ret)+sizeof(_pg_return.type), sizeof(pg_ret)-sizeof(_pg_return.type))) return; rsp_id = pg_ret.id; _pg_return.e.retdata = ntohl(pg_ret.data); } break; case PG_RESPONSE_EVENT: { /* Event */ struct pgresponse_event pg_ev; /* Read the rest of the event (already have response type) */ if (_pg_recv(((char*)&pg_ev)+sizeof(_pg_return.type), sizeof(pg_ev)-sizeof(_pg_return.type))) return; pg_ev.type = _pg_return.type; memset(&_pg_return.e.event,0,sizeof(_pg_return.e.event)); _pg_return.e.event.type = ntohs(pg_ev.event); _pg_return.e.event.from = ntohl(pg_ev.from); pg_ev.param = ntohl(pg_ev.param); /* Decode the event differently based on the type */ switch (_pg_return.e.event.type & PG_EVENTCODINGMASK) { /* Easy, da? */ default: case PG_EVENTCODING_PARAM: _pg_return.e.event.e.param = pg_ev.param; break; /* On some architectures this isn't necessary due to the layout * of the union. Hope the optimizer notices :) */ case PG_EVENTCODING_XY: case PG_EVENTCODING_KBD: /* Same thing, just different names */ _pg_return.e.event.e.size.w = pg_ev.param >> 16; _pg_return.e.event.e.size.h = pg_ev.param & 0xFFFF; break; /* Decode 'mouse' parameters, sign extend the 12-bit values to 16 bits */ case PG_EVENTCODING_PNTR: _pg_return.e.event.e.pntr.x = pg_ev.param & 0x0FFF; if (_pg_return.e.event.e.pntr.x & 0x0800) _pg_return.e.event.e.pntr.x |= 0xF000; _pg_return.e.event.e.pntr.y = (pg_ev.param>>12) & 0x0FFF; if (_pg_return.e.event.e.pntr.y & 0x0800) _pg_return.e.event.e.pntr.y |= 0xF000; _pg_return.e.event.e.pntr.btn = pg_ev.param >> 28; _pg_return.e.event.e.pntr.chbtn = (pg_ev.param>>24) & 0x000F; break; /* Transfer extra data */ case PG_EVENTCODING_DATA: _pg_return.e.event.e.data.size = pg_ev.param; if (!(_pg_return.e.event.e.data.pointer = _pg_malloc(_pg_return.e.event.e.data.size+1))) return; if (_pg_recv(_pg_return.e.event.e.data.pointer, _pg_return.e.event.e.data.size)) return; /* Add a null terminator */ ((char *)_pg_return.e.event.e.data.pointer) [_pg_return.e.event.e.data.size] = 0; /* If this was a valid PG_NWE_INFILTER event, stick the data in its place */ if (pg_ev.param == sizeof(union pg_client_trigger) && _pg_return.e.event.type == PG_NWE_INFILTER) { int i; _pg_return.e.event.e.data.trigger = (union pg_client_trigger*) _pg_return.e.event.e.data.pointer; for (i=0;i<(sizeof(_pg_return.e.event.e.data.trigger->array)/ sizeof(_pg_return.e.event.e.data.trigger->array[0]));i++) _pg_return.e.event.e.data.trigger->array[i] = ntohl(_pg_return.e.event.e.data.trigger->array[i]); } break; /* Decode keyboard event */ } } break; case PG_RESPONSE_DATA: { /* Something larger- return it in a dynamically allocated buffer */ struct pgresponse_data pg_data; /* Read the rest of the response (already have response type) */ pg_data.type = _pg_return.type; if (_pg_recv(((char*)&pg_data)+sizeof(_pg_return.type), sizeof(pg_data)-sizeof(_pg_return.type))) return; rsp_id = pg_data.id; _pg_return.e.data.size = ntohl(pg_data.size); if (!(_pg_return.e.data.data = _pg_malloc(_pg_return.e.data.size+1))) return; if (_pg_return.e.data.size) { if (_pg_recv(_pg_return.e.data.data,_pg_return.e.data.size)) return; } /* Add a null terminator */ ((char *)_pg_return.e.data.data)[_pg_return.e.data.size] = 0; } break; default: clienterr("Unexpected response type"); } #ifdef DEBUG if(rsp_id && (rsp_id != _pgrequestid)) { /* ID mismatch. This shouldn't happen, but if it does it's probably not serious. */ printf("PicoGUI - incorrect packet ID (%i -> %i)\n",_pgrequestid,rsp_id); } #endif #ifdef DEBUG_EVT if (_pg_return.type == PG_RESPONSE_EVENT) printf("Event is %d after case\n", _pg_return.e.event.type); #endif /* If there was an extra packet (left over from a 'ping') * we should suck that up now */ if (extra_packet) { struct pgresponse_ret cruft; _pg_recv(&cruft,sizeof(cruft)); } }
int t1_encrypt(int type, BUFFER *message, char *chainstr, int latency, BUFFER *ek, BUFFER *feedback) { BUFFER *b, *rem, *dest, *line, *field, *content; REMAILER remailer[MAXREM]; int badchains[MAXREM][MAXREM]; int maxrem, chainlen = 0; int chain[20]; int hop; int hashmark = 0; int err = 0; b = buf_new(); rem = buf_new(); dest = buf_new(); line = buf_new(); field = buf_new(); content = buf_new(); maxrem = t1_rlist(remailer, badchains); if (maxrem < 1) { clienterr(feedback, "No remailer list!"); err = -1; goto end; } chainlen = chain_select(chain, chainstr, maxrem, remailer, 1, line); if (chainlen < 1) { if (line->length) clienterr(feedback, line->data); else clienterr(feedback, "Invalid remailer chain!"); err = -1; goto end; } if (chain[0] == 0) chain[0] = chain_randfinal(type, remailer, badchains, maxrem, 1, chain, chainlen, 0); if (chain[0] == -1) { clienterr(feedback, "Invalid remailer chain!"); err = -1; goto end; } if (chain_rand(remailer, badchains, maxrem, chain, chainlen, 1, 0) == -1) { clienterr(feedback, "No reliable remailers!"); err = -1; goto end; } while (buf_getheader(message, field, content) == 0) { hdr_encode(content, 0); if (type == MSG_POST && bufieq(field, "newsgroups") && remailer[chain[0]].flags.post) { buf_appendf(dest, "Anon-Post-To: %b\n", content); } else if (type == MSG_MAIL && bufieq(field, "to")) { buf_appendf(dest, "Anon-To: %b\n", content); } else { /* paste header */ if (type == MSG_POST && bufieq(field, "newsgroups")) buf_appendf(dest, "Anon-To: %s\n", MAILtoNEWS); if (hashmark == 0) { buf_appends(b, "##\n"); hashmark = 1; } buf_appendheader(b, field, content); } } buf_nl(b); buf_rest(b, message); buf_move(message, b); if (type != MSG_NULL && dest->length == 0) { clienterr(feedback, "No destination address!"); err = -1; goto end; } if (type == MSG_NULL) { buf_sets(dest, "Null:\n"); } for (hop = 0; hop < chainlen; hop++) { if (hop == 0) { buf_sets(b, "::\n"); buf_cat(b, dest); } else { buf_sets(b, "::\nAnon-To: "); buf_appends(b, remailer[chain[hop - 1]].addr); buf_nl(b); } if (remailer[chain[hop]].flags.latent && latency > 0) buf_appendf(b, "Latent-Time: +%d:00r\n", latency); if (ek && remailer[chain[hop]].flags.ek) { t1_ek(line, ek, hop); buf_appendf(b, "Encrypt-Key: %b\n", line); } buf_nl(b); buf_cat(b, message); #ifdef USE_PGP if (remailer[chain[hop]].flags.pgp) { buf_clear(message); buf_clear(rem); buf_setf(rem, "<%s>", remailer[chain[hop]].addr); err = pgp_encrypt(PGP_ENCRYPT | PGP_REMAIL | PGP_TEXT, b, rem, NULL, NULL, NULL, NULL); if (err < 0) { buf_setf(line, "No PGP key for remailer %s!\n", remailer[chain[hop]].name); clienterr(feedback, line->data); goto end; } buf_appends(message, "::\nEncrypted: PGP\n\n"); buf_cat(message, b); } else #endif /* USE_PGP */ { if (remailer[chain[hop]].flags.pgponly) { buf_setf(line, "PGP encryption needed for remailer %s!\n", remailer[chain[hop]].name); clienterr(feedback, line->data); goto end; } buf_move(message, b); } if (ek && remailer[chain[hop]].flags.ek) buf_appends(message, "\n**\n"); } buf_clear(b); if (chainlen == 0) { buf_appends(b, "::\n"); buf_cat(b, dest); } else { buf_appendf(b, "%s: %s\n", ek ? "::\nAnon-To" : "To", remailer[chain[chainlen - 1]].addr); } buf_nl(b); buf_cat(b, message); buf_move(message, b); end: buf_free(b); buf_free(rem); buf_free(dest); buf_free(line); buf_free(field); buf_free(content); return (err); }