On Wed, Sep 23, 2009 at 12:53:35AM +0200, Mark Edgar wrote:
* Fix buffer overrun when using strncpy()
It's really sad to see code using strncpy.
* Use startswith() macro instead strncmp()
Macros don't go over so well around here, but I'm in favor of
that one (even if as a utility function rather than a macro).
* Use const char * where appropriate
const char* tends not to go over so well around here.
* Use size_t instead of unsigned int in readl().
We also tend not to use *_t integer types types in favor of int
and uint.
I'll take this opportunity to re-submit my ages-old
modifications of sic (which push the line count to 251).
Incidentally, what's with all of the (void) casts of function
calls lately? Have we really resorted to that level of GNU-style
hair brained pedantry?
--
Kris Maglione
The best way to predict the future is to invent it.
--Alan Kay
/* ? 2005-2007 Anselm R. Garbe <garbeam at gmail dot com>
* ? 2007 Kris Maglione <[email protected]>
* ? 2005 Nico Golde <nico at ngolde dot de>
* See LICENSE file for license details.
*/
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define nil ((void*)0)
typedef unsigned short ushort;
#define PINGTIMEOUT 300
static char* host = "irc.oftc.net";
static ushort port = 6667;
static char* password;
static char nick[32];
static char bufin[4096];
static char bufout[4096];
static char channel[256];
static time_t trespond;
static FILE *srv;
#define va_buf(buf, fmt) {\
va_list ap; \
\
va_start(ap, fmt); \
vsnprintf(buf, sizeof buf, fmt, ap); \
va_end(ap); \
}
#include "util.c"
static void
pout(char *channel, char *fmt, ...) {
static char timestr[18];
time_t t;
va_buf(bufout, fmt);
t = time(nil);
strftime(timestr, sizeof timestr, "%D %R", localtime(&t));
fprintf(stdout, "%-12s: %s %s\n", channel, timestr, bufout);
}
static void
sout(char *fmt, ...) {
va_buf(bufout, fmt);
fprintf(srv, "%s\r\n", bufout);
}
static void
privmsg(char *channel, char *msg) {
if(channel[0] == '\0') {
pout("", "No channel to send to");
return;
}
pout(channel, "<%s> %s", nick, msg);
sout("PRIVMSG %s :%s", channel, msg);
}
static void
parsein(char *msg) {
char *p;
char c;
if(msg[0] == '\0')
return;
msg = ctok(&msg, '\n');
if(msg[0] != ':') {
privmsg(channel, msg);
return;
}
c = *++msg;
if(!c || !isspace(msg[1]))
sout("%s", msg);
else {
if(msg[1])
msg += 2;
switch(c) {
case 'j':
sout("JOIN %s", msg);
if(channel[0] == '\0')
strlcpy(channel, msg, sizeof channel);
break;
case 'l':
p = tok(&msg);
if(!*p)
p = channel;
if(!*msg)
msg = "sic - 250 LOC are too much!";
sout("PART %s :%s", p, msg);
break;
case 'm':
p = tok(&msg);
privmsg(p, msg);
break;
case 's':
strlcpy(channel, msg, sizeof channel);
break;
default:
sout("%c %s", c, msg);
break;
}
}
}
static void
parsesrv(char *msg) {
char *cmd, *p, *usr, *txt;
usr = host;
if(!msg || !*msg)
return;
if(msg[0] == ':') {
msg++;
p = tok(&msg);
if(!*msg)
return;
usr = ctok(&p, '!');
}
txt = ctok(&msg, '\r');
msg = ctok(&txt, ':');
cmd = tok(&msg);
if(!strcmp("PONG", cmd))
return;
if(!strcmp("PRIVMSG", cmd))
pout(msg, "<%s> %s", usr, txt);
else if(!strcmp("PING", cmd))
sout("PONG %s", txt);
else {
pout(usr, ">< %s: %s", cmd, txt);
if(!strcmp("NICK", cmd) && !strcmp(usr, nick))
strlcpy(nick, txt, sizeof nick);
}
}
int
main(int argc, char *argv[]) {
int i, c;
struct timeval tv;
fd_set rd;
strlcpy(nick, getenv("USER"), sizeof nick);
for(i = 1; i < argc; i++) {
c = argv[i][1];
if(argv[i][0] != '-' || argv[i][2])
c = -1;
switch(c) {
case 'h':
if(++i < argc) host = argv[i];
break;
case 'p':
if(++i < argc) port = atoi(argv[i]);
break;
case 'n':
if(++i < argc) strlcpy(nick, argv[i], sizeof nick);
break;
case 'k':
if(++i < argc) password = argv[i];
break;
case 'v':
eprint("sic-"VERSION", ? 2005-2007 Anselm R. Garbe, Nico Golde\n");
default:
eprint("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n");
}
}
/* init */
i = dial(host, port);
srv = fdopen(i, "r+");
/* login */
if(password)
sout("PASS %s", password);
sout("NICK %s", nick);
sout("USER %s localhost %s :%s", nick, host, nick);
fflush(srv);
setbuf(stdout, nil);
setbuf(srv, nil);
for(;;) { /* main loop */
FD_ZERO(&rd);
FD_SET(0, &rd);
FD_SET(fileno(srv), &rd);
tv.tv_sec = 120;
tv.tv_usec = 0;
i = select(fileno(srv) + 1, &rd, 0, 0, &tv);
if(i < 0) {
if(errno == EINTR)
continue;
eprint("sic: error on select():");
}
else if(i == 0) {
if(time(nil) - trespond >= PINGTIMEOUT)
eprint("sic shutting down: parse timeout\n");
sout("PING %s", host);
continue;
}
if(FD_ISSET(fileno(srv), &rd)) {
if(fgets(bufin, sizeof bufin, srv) == nil)
eprint("sic: remote host closed connection\n");
parsesrv(bufin);
trespond = time(nil);
}
if(FD_ISSET(0, &rd)) {
if(fgets(bufin, sizeof bufin, stdin) == nil)
eprint("sic: broken pipe\n");
parsein(bufin);
}
}
return 0;
}
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
static void
eprint(const char *fmt, ...) {
va_buf(bufout, fmt);
fprintf(stderr, "%s", bufout);
if(fmt[0] && fmt[strlen(fmt)-1] == ':')
fprintf(stderr, " %s\n", strerror(errno));
exit(1);
}
static int
dial(char *host, int port) {
struct hostent *hp;
static struct sockaddr_in addr;
int i;
if((i = socket(AF_INET, SOCK_STREAM, 0)) < 0)
eprint("sic: cannot connect host '%s':", host);
if(nil == (hp = gethostbyname(host)))
eprint("sic: cannot resolve hostname '%s': %s\n", host, hstrerror(h_errno));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
if(connect(i, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)))
eprint("sic: cannot connect host '%s':", host);
return i;
}
#define strlcpy _strlcpy
static void
strlcpy(char *to, const char *from, int l) {
strncpy(to, from, l-1);
to[l-1] = '\0';
}
static void
eat(char **s, int (*p)(int), int r) {
char *q;
for(q=*s; *q && p(*q) == r; q++)
;
*s = q;
}
static char*
tok(char **s) {
char *p;
eat(s, isspace, 1);
p = *s;
eat(s, isspace, 0);
if(**s) *(*s)++ = '\0';
return p;
}
static char*
ctok(char **s, int c) {
char *p, *q;
q = *s;
for(p=q; *p && *p != c; p++)
;
if(*p) *p++ = '\0';
*s = p;
return q;
}