aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2016-01-22 17:03:05 -0500
committerFranklin Wei <git@fwei.tk>2016-01-22 17:03:05 -0500
commitb33d9c81c116d43b38ceb8b247bd77f9736574b9 (patch)
treeb92a923ffc29ee438c646f4b2b08f5bd588dee2d
parenta486d4b5e1679e0ddf5a4afa661501afefe4a854 (diff)
downloadnetcosm-b33d9c81c116d43b38ceb8b247bd77f9736574b9.zip
netcosm-b33d9c81c116d43b38ceb8b247bd77f9736574b9.tar.gz
netcosm-b33d9c81c116d43b38ceb8b247bd77f9736574b9.tar.bz2
netcosm-b33d9c81c116d43b38ceb8b247bd77f9736574b9.tar.xz
PuTTY compatibility, lots of other stuff
-rw-r--r--Makefile7
-rw-r--r--README.md19
-rw-r--r--src/client.c64
-rw-r--r--src/hash.h4
-rw-r--r--src/room.c59
-rw-r--r--src/room.h49
-rw-r--r--src/server.c58
-rw-r--r--src/server_reqs.c33
-rw-r--r--src/server_reqs.h1
-rw-r--r--src/telnet.c106
-rw-r--r--src/telnet.h23
11 files changed, 206 insertions, 217 deletions
diff --git a/Makefile b/Makefile
index a39c102..944b786 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ PLATFORM = unix
NETCOSM_SRC = $(shell cat SOURCES)
NETCOSM_OBJ := $(NETCOSM_SRC:.c=.o)
-CFLAGS = -O3 -g -I src/ -I export/include -Wall -Wextra -Wshadow \
+CFLAGS = -Og -g -I src/ -I export/include -Wall -Wextra -Wshadow \
-std=c99 -fno-strict-aliasing
LDFLAGS = -lev -lssl -lcrypto
@@ -67,3 +67,8 @@ help:
@echo " depclean - Remove dependency files"
@echo " veryclean - Remove object and dependency files"
@echo "Build targets:"
+
+.PHONY: setcap
+setcap:
+ @echo "Enabling CAP_NET_BIND_SERVICE on "$(OUT)/$(PLATFORM).bin"..."
+ @sudo setcap 'cap_net_bind_service=+ep' $(OUT)/$(PLATFORM).bin
diff --git a/README.md b/README.md
index d784373..bbf3a74 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,9 @@ features might drift out of existence without prior warning!
### Prerequisites:
-* libgcrypt
+* openssl
* libev
-* linux >= 3.4 (need "packet mode" pipes)
+* linux >= 3.4 (for "packet mode" pipes)
* glibc >= 2.9
### Compiling
@@ -19,6 +19,17 @@ features might drift out of existence without prior warning!
This gives you the executable in `build/unix.bin`.
+### Running
+
+If you want to listen on a privileged port (below 1024), you will
+either have to run the executable as root (not recommended), or set
+the CAP_NET_BIND_SERVICE capability on Linux:
+
+ sudo setcap 'cap_net_bind_service=+ep' build/unix.bin
+
+If running as root, you will need an unprivileged user called 'nobody'
+on your system in order for things to work.
+
## Todo List
* World persistence (partial)
@@ -29,6 +40,8 @@ This gives you the executable in `build/unix.bin`.
## Internal Design
+### Child-Master Requests
+
A child process is spawned for every client that connects. There are
two pipes created for every child: a pipe for the child to write to,
and a pipe for the master to write to.
@@ -36,8 +49,6 @@ and a pipe for the master to write to.
Both of these pipes are created in "packet mode" (see pipe(2)), and
therefore require at least linux 3.4 and glibc 2.9.
-### Child-Master Requests
-
Many operations, such as listing clients, require the help of the
master process. To request an operation, the child writes it's request
data to it's pipe to the parent. The size of the request MUST be less
diff --git a/src/client.c b/src/client.c
index 2ad7256..0cfb0f9 100644
--- a/src/client.c
+++ b/src/client.c
@@ -72,9 +72,10 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...)
/* do some line wrapping */
int pos = 0, last_space = 0;
- char newline = '\n';
char *ptr = buf;
- uint16_t line_width = telnet_get_width();
+ uint16_t line_width = telnet_get_width() + 1;
+ char *line_buf = malloc(line_width + 2);
+ size_t line_idx = 0;
while(ptr[pos])
{
bool is_newline = (ptr[pos] == '\n');
@@ -82,12 +83,21 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...)
{
if(is_newline || !last_space)
last_space = pos;
+
while(*ptr && last_space-- > 0)
- out_raw(ptr++, 1);
- out_raw(&newline, 1);
+ {
+ line_buf[line_idx++] = *ptr++;
+ }
+
+ line_buf[line_idx++] = '\r';
+ line_buf[line_idx++] = '\n';
+
+ out_raw(line_buf, line_idx);
+ line_idx = 0;
+
if(is_newline)
- ++ptr; /* skip newline */
- while(*ptr && *ptr == ' ')
+ ++ptr; /* skip the newline */
+ while(*ptr == ' ')
++ptr;
last_space = 0;
pos = 0;
@@ -100,6 +110,7 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...)
}
}
out_raw(ptr, strlen(ptr));
+ free(line_buf);
}
static volatile sig_atomic_t request_complete;
@@ -155,6 +166,8 @@ void send_master(unsigned char cmd, const void *data, size_t sz)
while(!request_complete) poll_requests();
free(req);
+
+ debugf("done with request\n");
}
#define BUFSZ 128
@@ -162,10 +175,12 @@ void send_master(unsigned char cmd, const void *data, size_t sz)
char *client_read(void)
{
char *buf;
-
+ size_t bufidx;
tryagain:
buf = malloc(BUFSZ);
+ bufidx = 0;
+
memset(buf, 0, BUFSZ);
/* set of the client fd and the pipe from our parent */
@@ -193,20 +208,27 @@ tryagain:
}
else if(fds[i].fd == client_fd)
{
- ssize_t len = read(client_fd, buf, BUFSZ - 1);
+ ssize_t len = read(client_fd, buf + bufidx, BUFSZ - bufidx - 1);
if(len < 0)
error("lost connection");
buf[BUFSZ - 1] = '\0';
- enum telnet_status ret = telnet_parse_data((unsigned char*)buf, len);
+ enum telnet_status ret = telnet_parse_data((unsigned char*)buf + bufidx, len);
- if(ret != TELNET_DATA)
+ switch(ret)
{
+ case TELNET_EXIT:
+ case TELNET_FOUNDCMD:
free(buf);
if(ret == TELNET_EXIT)
exit(0);
goto tryagain;
+ case TELNET_DATA:
+ bufidx += len;
+ continue;
+ case TELNET_LINEOVER:
+ break;
}
remove_cruft(buf);
@@ -268,6 +290,8 @@ bool poll_requests(void)
unsigned char cmd = packet[0];
+ debugf("Child gets code %d\n", cmd);
+
switch(cmd)
{
case REQ_BCASTMSG:
@@ -348,6 +372,8 @@ void client_change_room(room_id id)
send_master(REQ_SETROOM, &id, sizeof(id));
}
+void *dir_map = NULL;
+
void client_move(const char *dir)
{
const struct dir_pair {
@@ -377,14 +403,13 @@ void client_move(const char *dir)
{ "IN", DIR_IN },
{ "OUT", DIR_OT },
};
- static void *map = NULL;
- if(!map)
+ if(!dir_map)
{
- map = hash_init(ARRAYLEN(dirs), hash_djb, compare_strings);
- hash_insert_pairs(map, (struct hash_pair*)dirs, sizeof(struct dir_pair), ARRAYLEN(dirs));
+ dir_map = hash_init(ARRAYLEN(dirs), hash_djb, compare_strings);
+ hash_insert_pairs(dir_map, (struct hash_pair*)dirs, sizeof(struct dir_pair), ARRAYLEN(dirs));
}
- struct dir_pair *pair = hash_lookup(map, dir);
+ struct dir_pair *pair = hash_lookup(dir_map, dir);
if(pair)
{
send_master(REQ_MOVE, &pair->val, sizeof(pair->val));
@@ -416,6 +441,8 @@ void client_main(int fd, struct sockaddr_in *addr, int total, int to, int from)
debugf("New client %s\n", ip);
debugf("Total clients: %d\n", total);
+ debugf("client is running with uid %d\n", getuid());
+
auth:
out("NetCosm " NETCOSM_VERSION "\n");
@@ -601,8 +628,15 @@ auth:
else if(!strcmp(what, "KICK"))
{
char *pid_s = strtok_r(NULL, WSPACE, &save);
+ all_upper(pid_s);
if(pid_s)
{
+ if(!strcmp(pid_s, "ALL"))
+ {
+ const char *msg = "Kicking everyone...\n";
+ send_master(REQ_KICKALL, msg, strlen(msg));
+ goto next_cmd;
+ }
/* weird pointer voodoo */
/* TODO: simplify */
char pidbuf[MAX(sizeof(pid_t), MSG_MAX)];
diff --git a/src/hash.h b/src/hash.h
index e7fbe00..21da10a 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -41,8 +41,8 @@ void hash_setfreekey_cb(void*, void (*cb)(void *key));
void hash_free(void*);
/*
- * insert a pair, returns NULL if not already found, otherwise returns
- * the existing data pointer
+ * insert a pair, returns NULL if NOT already found, otherwise returns
+ * the existing data pointer without inserting the new pair
*/
void *hash_insert(void*, const void *key, const void *data);
diff --git a/src/room.c b/src/room.c
index e0be4fd..7596dc4 100644
--- a/src/room.c
+++ b/src/room.c
@@ -21,8 +21,10 @@
#include "hash.h"
#include "server.h"
#include "room.h"
+#include "userdb.h"
/* processed world data */
+
static struct room_t *world;
static size_t world_sz;
static char *world_name;
@@ -40,51 +42,17 @@ bool room_user_add(room_id id, struct child_data *child)
if(!room)
error("unknown room %d", id);
- struct user_t *iter = room->users, *last = NULL;
- while(iter)
- {
- if(iter->data->pid == child->pid)
- return false;
- last = iter;
- iter = iter->next;
- }
-
- struct user_t *new = calloc(sizeof(struct user_t), 1);
-
- new->data = child;
- new->next = NULL;
-
- if(last)
- last->next = new;
+ if(hash_insert(room->users, &child->pid, child))
+ return false;
else
- room->users = new;
-
- ++room->num_users;
-
- return true;
+ return true;
}
bool room_user_del(room_id id, struct child_data *child)
{
struct room_t *room = room_get(id);
- struct user_t *iter = room->users, *last = NULL;
- while(iter)
- {
- if(iter->data->pid == child->pid)
- {
- if(last)
- last->next = iter->next;
- else
- room->users = iter->next;
- free(iter);
- --room->num_users;
- return true;
- }
- last = iter;
- iter = iter->next;
- }
- return false;
+ return hash_remove(room->users, &child->pid);
}
void write_roomid(int fd, room_id *id)
@@ -143,12 +111,8 @@ void world_save(const char *fname)
static void room_free(struct room_t *room)
{
- while(room->users)
- {
- struct user_t *old = room->users;
- room->users = room->users->next;
- free(old);
- }
+ hash_free(room->users);
+ room->users = NULL;
free(room->data.name);
free(room->data.desc);
}
@@ -168,6 +132,9 @@ void world_free(void)
}
}
+static SIMP_HASH(pid_t, pid_hash);
+static SIMP_EQUAL(pid_t, pid_equal);
+
/**
* Loads a world using data on disk and in memory.
*
@@ -205,6 +172,8 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz
for(unsigned i = 0; i < world_sz; ++i)
{
+ world[i].users = hash_init((userdb_size() + 1) / 2, pid_hash, pid_equal);
+
world[i].id = read_roomid(fd);
memcpy(&world[i].data, data + i, sizeof(struct roomdata_t));
world[i].data.name = read_string(fd);
@@ -255,6 +224,8 @@ void world_init(const struct roomdata_t *data, size_t sz, const char *name)
}
}
+ world[i].users = hash_init((userdb_size() + 1) / 2, pid_hash, pid_equal);
+
world_sz = i + 1;
}
diff --git a/src/room.h b/src/room.h
index 68ee413..c973b6a 100644
--- a/src/room.h
+++ b/src/room.h
@@ -18,16 +18,18 @@
#pragma once
+/* Our world is an array of rooms, each having a list of objects in
+ them, as well as actions that can be performed in the room. Objects
+ are added by hooks in rooms, which are provided by the world
+ module. */
+
typedef enum room_id { ROOM_NONE = -1 } room_id;
typedef unsigned __int128 obj_id;
-enum direction_t { DIR_N = 0, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW, DIR_UP, DIR_DN, DIR_IN, DIR_OT, NUM_DIRECTIONS };
+typedef struct child_data user_t;
-struct user_t {
- struct child_data *data;
- struct user_t *next;
-};
+enum direction_t { DIR_N = 0, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW, DIR_UP, DIR_DN, DIR_IN, DIR_OT, NUM_DIRECTIONS };
/* the data we get from a world module */
struct roomdata_t {
@@ -41,30 +43,33 @@ struct roomdata_t {
const char * const adjacent[NUM_DIRECTIONS];
void (* const hook_init)(room_id id);
- void (* const hook_enter)(room_id room, struct user_t *user);
- void (* const hook_leave)(room_id room, struct user_t *user);
+ void (* const hook_enter)(room_id room, user_t *user);
+ void (* const hook_leave)(room_id room, user_t *user);
};
struct object_t {
obj_id id;
- const char *class;
+
const char *name; /* no articles: "a", "an", "the" */
- bool proper; /* whether to use "the" in describing this object */
void *userdata;
+ bool can_take;
+ bool list;
+
void (*hook_serialize)(int fd, struct object_t*);
- void (*hook_take)(struct object_t*, struct user_t *user);
- void (*hook_drop)(struct object_t*, struct user_t *user);
- void (*hook_use)(struct object_t*, struct user_t *user);
+ void (*hook_take)(struct object_t*, user_t *user);
+ void (*hook_drop)(struct object_t*, user_t *user);
+ void (*hook_use)(struct object_t*, user_t *user);
void (*hook_destroy)(struct object_t*);
+ char* (*hook_desc)(struct object_t*, user_t*);
};
struct verb_t {
const char *name;
/* toks is strtok_r's pointer */
- void (*execute)(const char *toks, struct user_t *user);
+ void (*execute)(const char *toks, user_t *user);
};
struct room_t {
@@ -73,16 +78,10 @@ struct room_t {
room_id adjacent[NUM_DIRECTIONS];
- /* arrays instead of linked lists because insertion should be rare for these */
- size_t objects_sz;
- struct object_t *objects;
-
- size_t verbs_sz;
- struct verb_t *verbs;
-
- /* linked list for users, random access is rare */
- struct user_t *users;
- int num_users;
+ /* hash maps */
+ void *objects;
+ void *verbs;
+ void *users; /* PID -> user_t */
};
/* room/world */
@@ -94,6 +93,10 @@ struct room_t *room_get(room_id id);
bool room_user_add(room_id id, struct child_data *child);
bool room_user_del(room_id id, struct child_data *child);
+/* returns a new object */
struct object_t *obj_new(void);
+/* new should point to a statically allocated object */
+void obj_add(room_id room, struct object_t *new);
+
void world_free(void);
diff --git a/src/server.c b/src/server.c
index 4430f7e..6ce4de4 100644
--- a/src/server.c
+++ b/src/server.c
@@ -103,28 +103,10 @@ static void handle_client(int fd, struct sockaddr_in *addr,
static void __attribute__((noreturn)) serv_cleanup(void)
{
- debugf("Shutdown server.\n");
-
- /* kill all our children (usually init claims them and wait()'s
- for them, but not always) */
- if(child_map)
- {
- struct sigaction sa;
- sigfillset(&sa.sa_mask);
- sigaddset(&sa.sa_mask, SIGCHLD);
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = 0;
- sigaction(SIGCHLD, &sa, NULL); /* kill all children */
- void *ptr = child_map, *save;
- do {
- struct child_data *child = hash_iterate(ptr, &save, NULL);
- if(!child)
- break;
- ptr = NULL;
- kill(child->pid, SIGKILL);
- } while(1);
- handle_disconnects();
- }
+ if(!are_child)
+ debugf("Shutdown server.\n");
+ else
+ debugf("Shutdown worker.\n");
if(shutdown(server_socket, SHUT_RDWR) > 0)
error("shutdown");
@@ -136,6 +118,10 @@ static void __attribute__((noreturn)) serv_cleanup(void)
hash_free(child_map);
child_map = NULL;
+ extern void *dir_map;
+ hash_free(dir_map);
+ dir_map = NULL;
+
userdb_shutdown();
extern char *current_user;
@@ -150,7 +136,7 @@ static void __attribute__((noreturn)) serv_cleanup(void)
static void __attribute__((noreturn)) sigint_handler(int s)
{
(void) s;
- serv_cleanup();
+ exit(0);
}
static void check_userfile(void)
@@ -208,9 +194,6 @@ static int server_bind(void)
return sock;
}
-static SIMP_HASH(pid_t, pid_hash);
-static SIMP_EQUAL(pid_t, pid_equal);
-
static void childreq_cb(EV_P_ ev_io *w, int revents)
{
(void) EV_A;
@@ -337,6 +320,9 @@ static void init_signals(void)
error("sigaction");
}
+static SIMP_HASH(pid_t, pid_hash);
+static SIMP_EQUAL(pid_t, pid_equal);
+
int server_main(int argc, char *argv[])
{
debugf("*** Starting NetCosm %s (libev %d.%d, %s) ***\n",
@@ -353,6 +339,7 @@ int server_main(int argc, char *argv[])
srand(time(0));
server_socket = server_bind();
+
userdb_init(USERFILE);
check_userfile();
@@ -374,28 +361,13 @@ int server_main(int argc, char *argv[])
* because libev grabs SIGCHLD */
init_signals();
- /* drop root privileges */
- if(getuid() == 0)
- {
- struct passwd *nobody = getpwnam("nobody");
- if(!nobody)
- error("couldn't get unprivileged user");
- if(setgid(nobody->pw_gid) != 0)
- error("setgid");
- if(setuid(nobody->pw_uid) != 0)
- error("setuid");
- if(setuid(0) >= 0)
- error("failed to drop root");
- if(access(USERFILE, R_OK) >= 0)
- error("failed to drop root");
- debugf("Dropped root privileges.\n");
- }
-
ev_io server_watcher;
ev_io_init(&server_watcher, new_connection_cb, server_socket, EV_READ);
ev_set_priority(&server_watcher, EV_MAXPRI);
ev_io_start(EV_A_ &server_watcher);
+ atexit(serv_cleanup);
+
ev_loop(loop, 0);
/* should never get here */
diff --git a/src/server_reqs.c b/src/server_reqs.c
index c7e9f09..bf661b9 100644
--- a/src/server_reqs.c
+++ b/src/server_reqs.c
@@ -30,7 +30,13 @@ static void send_packet(struct child_data *child, unsigned char cmd,
pkt[0] = cmd;
if(datalen)
memcpy(pkt + 1, data, datalen);
- write(child->outpipe[1], pkt, datalen + 1);
+tryagain:
+ if(write(child->outpipe[1], pkt, datalen + 1) < 0)
+ {
+ /* write can fail, so we try again */
+ if(errno == EAGAIN)
+ goto tryagain;
+ }
}
static void req_pass_msg(unsigned char *data, size_t datalen,
@@ -217,6 +223,13 @@ static void req_send_geninfo(unsigned char *data, size_t datalen, struct child_d
send_packet(sender, REQ_BCASTMSG, buf, len);
}
+static void req_kick_always(unsigned char *data, size_t datalen,
+ struct child_data *sender, struct child_data *child)
+{
+ (void) sender;
+ send_packet(child, REQ_KICK, data, datalen);
+}
+
static const struct child_request {
unsigned char code;
@@ -245,6 +258,7 @@ static const struct child_request {
{ REQ_GETUSERDATA, true, CHILD_NONE, NULL, req_send_user, },
{ REQ_DELUSERDATA, true, CHILD_NONE, NULL, req_del_user, },
{ REQ_ADDUSERDATA, true, CHILD_NONE, NULL, req_add_user, },
+ { REQ_KICKALL, true, CHILD_ALL_BUT_SENDER, req_kick_always, NULL },
//{ REQ_ROOMMSG, true, CHILD_ALL, req_send_room_msg, NULL, },
};
@@ -289,6 +303,8 @@ bool handle_child_req(int in_fd)
ssize_t packet_len = read(in_fd, packet, MSG_MAX);
+ struct child_data *sender = NULL;
+
if(packet_len <= 0)
goto fail;
@@ -296,7 +312,7 @@ bool handle_child_req(int in_fd)
memcpy(&sender_pid, packet, sizeof(pid_t));
debugf("servreq: Got request from PID %d\n", sender_pid);
- struct child_data *sender = hash_lookup(child_map, &sender_pid);
+ sender = hash_lookup(child_map, &sender_pid);
if(!sender)
{
@@ -343,6 +359,8 @@ bool handle_child_req(int in_fd)
if(child->pid == sender->pid)
continue;
+ debugf("iterating over child %d\n", child->pid);
+
switch(req->which)
{
case CHILD_ALL:
@@ -356,12 +374,19 @@ bool handle_child_req(int in_fd)
finish:
+ debugf("finalizing request\n");
+
if(req && req->finalize)
req->finalize(data, datalen, sender);
- if(req)
+ /* fall through */
+fail:
+ if(sender)
+ {
send_packet(sender, REQ_ALLDONE, NULL, 0);
-fail:
+ debugf("sending all done code\n");
+ }
+
return true;
}
diff --git a/src/server_reqs.h b/src/server_reqs.h
index 06e3f46..6ab093b 100644
--- a/src/server_reqs.h
+++ b/src/server_reqs.h
@@ -37,6 +37,7 @@
#define REQ_ADDUSERDATA 15 /* server: insert user data; child: success/fail */
#define REQ_PRINTNEWLINE 16 /* child: print a newline */
#define REQ_ALLDONE 17 /* child: break out of send_master() */
+#define REQ_KICKALL 18 /* server: kick everyone except the sender */
/* child states, sent as an int to the master */
#define STATE_INIT 0 /* initial state */
diff --git a/src/telnet.c b/src/telnet.c
index c605c2e..9963007 100644
--- a/src/telnet.c
+++ b/src/telnet.c
@@ -19,6 +19,8 @@
#include "globals.h"
#include "client.h"
+
+#define TELCMDS
#include "telnet.h"
static uint16_t term_width, term_height;
@@ -38,6 +40,7 @@ enum telnet_status telnet_parse_data(const unsigned char *buf, size_t buflen)
bool iac = false;
bool found_cmd = false;
bool in_sb = false;
+ bool line_done = false;
debugf("telnet: ");
@@ -45,67 +48,50 @@ enum telnet_status telnet_parse_data(const unsigned char *buf, size_t buflen)
{
unsigned char c = buf[i];
- const struct telnet_cmd {
- int val;
- const char *name;
- } commands[] = {
- { IAC, "IAC" },
- { DONT, "DONT" },
- { DO, "DO" },
- { WONT, "WONT" },
- { WILL, "WILL" },
- { GA, "GA" },
- { AYT, "AYT" },
- { NOP, "NOP" },
- { SB, "SB" },
- { SE, "SE" },
- { ECHO, "ECHO" },
- { SGA, "SGA" },
- { STATUS, "STATUS" },
- { NAWS, "NAWS" },
- { IP, "IP" },
- };
-
if(c == IAC)
iac = true;
+ else if(c == '\n' || c == '\r')
+ {
+ debugf("found newline, done reading.\n");
+ line_done = true;
+ }
if(iac)
{
- for(unsigned int cmd_idx = 0; cmd_idx < ARRAYLEN(commands); ++cmd_idx)
+ if(TELCMD_OK(c))
{
- if(c == commands[cmd_idx].val)
+ debugf("%s ", TELCMD(c));
+ found_cmd = true;
+ switch(c)
{
- debugf("%s ", commands[cmd_idx].name);
- found_cmd = true;
- switch(c)
+ case IP:
+ return TELNET_EXIT;
+ case SB:
+ in_sb = true;
+ break;
+ case TELOPT_NAWS:
+ if(in_sb)
{
- case IP:
- return TELNET_EXIT;
- case SB:
- in_sb = true;
- break;
- case NAWS:
- if(in_sb)
+ /* read height/width */
+ uint8_t bytes[4];
+ int j = 0;
+ while(j < 4 && i < buflen)
{
- /* read height/width */
- uint8_t bytes[4];
- int j = 0;
- while(j < 4 && i < buflen)
+ bytes[j++] = buf[++i];
+ debugf("%d ", buf[j - 1]);
+ if(bytes[j - 1] == 255) /* 255 is doubled to distinguish from IAC */
{
- bytes[j++] = buf[++i];
- debugf("%d ", buf[j - 1]);
- if(bytes[j - 1] == 255) /* 255 is doubled to distinguish from IAC */
- {
- ++i;
- }
+ ++i;
}
- term_width = ntohs(*((uint16_t*)bytes));
- term_height = ntohs(*((uint16_t*)(bytes+2)));
}
- break;
+ if(i >= buflen && j != 4)
+ error("client SB NAWS command to short");
+ term_width = ntohs(*((uint16_t*)bytes));
+ term_height = ntohs(*((uint16_t*)(bytes+2)));
}
- goto got_cmd;
+ break;
}
+ goto got_cmd;
}
}
debugf("%d ", c);
@@ -114,15 +100,17 @@ enum telnet_status telnet_parse_data(const unsigned char *buf, size_t buflen)
}
debugf("\n");
+ if(found_cmd)
+ debugf("telnet: is NOT data\n");
- return found_cmd ? TELNET_FOUNDCMD : TELNET_DATA;
+ return found_cmd ? TELNET_FOUNDCMD :
+ (line_done ? TELNET_LINEOVER : TELNET_DATA);
}
void telnet_echo_off(void)
{
const unsigned char seq[] = {
- IAC, DONT, ECHO,
- IAC, WILL, ECHO,
+ IAC, WILL, TELOPT_ECHO,
};
out_raw(seq, ARRAYLEN(seq));
}
@@ -130,8 +118,7 @@ void telnet_echo_off(void)
void telnet_echo_on(void)
{
const unsigned char seq[] = {
- IAC, DO, ECHO,
- IAC, WONT, ECHO,
+ IAC, WONT, TELOPT_ECHO,
};
out_raw(seq, ARRAYLEN(seq));
}
@@ -139,18 +126,17 @@ void telnet_echo_on(void)
void telnet_init(void)
{
const unsigned char init_seq[] = {
- IAC, WONT, SGA,
- IAC, DONT, SGA,
+ IAC, WONT, TELOPT_SGA,
+ IAC, DONT, TELOPT_SGA,
- IAC, DO, NAWS,
+ IAC, DO, TELOPT_NAWS,
- IAC, WONT, STATUS,
- IAC, DONT, STATUS,
+ IAC, WONT, TELOPT_STATUS,
+ IAC, DONT, TELOPT_STATUS,
- IAC, DO, ECHO,
- IAC, WONT, ECHO,
+ IAC, DO, TELOPT_ECHO,
- IAC, DONT, LINEMODE,
+ IAC, DONT, TELOPT_LINEMODE,
};
term_width = 80;
term_height = 24;
diff --git a/src/telnet.h b/src/telnet.h
index 2289e84..8669e87 100644
--- a/src/telnet.h
+++ b/src/telnet.h
@@ -18,32 +18,13 @@
#pragma once
-/* commands */
-#define IAC 255
-#define DONT 254
-#define DO 253
-#define WONT 252
-#define WILL 251
-#define SB 250
-#define GA 249
-#define EL 248
-#define EC 247
-#define AYT 246
-#define IP 244
-#define NOP 241
-#define SE 240
-
-/* options */
-#define ECHO 1
-#define SGA 3
-#define STATUS 5
-#define NAWS 31
-#define LINEMODE 34
+#include <arpa/telnet.h>
void telnet_init(void);
enum telnet_status { TELNET_DATA = 0,
TELNET_FOUNDCMD,
+ TELNET_LINEOVER,
TELNET_EXIT };
enum telnet_status telnet_parse_data(const unsigned char*, size_t);