diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | gopher.c | 113 | ||||
-rw-r--r-- | gopher_client/client.c | 89 | ||||
-rw-r--r-- | gph.c | 288 | ||||
-rw-r--r-- | index.gph | 4 | ||||
-rw-r--r-- | log.c | 2 | ||||
-rw-r--r-- | main.c | 33 |
7 files changed, 492 insertions, 41 deletions
@@ -1,11 +1,11 @@ .POSIX: CC=cc -CFLAGS=-ansi -Wall -Wextra -pedantic +CFLAGS=-ansi -D_POSIX_C_SOURCE=200809L -Wall -Wextra -pedantic all: gopherd -SRC=main.c log.c gopher.c +SRC=main.c log.c gopher.c gph.c OBJ=$(SRC:.c=.o) gopherd: $(OBJ) @@ -1,23 +1,18 @@ -#define _POSIX_C_SOURCE 200112L - #include <sys/socket.h> +#include <sys/types.h> +#include <dirent.h> #include <stdio.h> #include <string.h> +#include <stdlib.h> #include "log.h" -static char *res = "1Entry 1 /entry1 localhost 3000\r\n\ -1Entry 2 /entry2 localhost 3000\r\n\ -1Entry 3 /entry3 localhost 3000\r\n\ -."; - -static char *res1 = "0SubEntry 1 /entry1 localhost 3000\r\n\ -0SubEntry 2 /entry2 localhost 3000\r\n\ -0SubEntry 3 /entry3 localhost 3000\r\n\ -."; +#define GOPHERROOT "/tmp/gopherroot" +#define GOPHERHOST "localhost" +#define GOPHERPORT "70" -static int +static int sendall(int fd, const char *buf, size_t len) { size_t sent = 0; @@ -35,22 +30,100 @@ sendall(int fd, const char *buf, size_t len) return 0; } -int +static int +print_indexfile(int fd, const char *path) +{ + FILE *f; + char buf[512]; + + f = fopen(path, "r"); + if (f == NULL) { + perror("fopen"); + return -1; + } + while (!feof(f)) { + if (fgets(buf, sizeof(buf), f) == NULL) + continue; + if (sendall(fd, buf, strlen(buf)) == -1) { + logmsg(LOG_INFO, "failed to send data"); + return -1; + fclose(f); + } + } + + fclose(f); + return 0; +} + +static int +print_dir(int fd, const char *path) +{ + DIR *root; + struct dirent *node; + char *buf; + int type; + size_t buflen; + + root = opendir(path); + if (root == NULL) { + perror("opendir"); + return -1; + } + while ((node = readdir(root)) != NULL) { + buflen = sizeof(type) + strlen(node->d_name) + + strlen(GOPHERROOT) + strlen(GOPHERHOST) + + strlen(GOPHERPORT) + 6; + if ((buf = malloc(buflen)) == NULL) { + perror("malloc"); + return -1; + } + + switch (node->d_type) { + case 4: + type = 1; + break; + case 8: + type = 0; + break; + } + + sprintf(buf, "%d%s %s %s %s\r\n", + type, node->d_name, GOPHERROOT, "localhost", "3000"); + if (sendall(fd, buf, buflen) == -1) { + logmsg(LOG_INFO, "failed to send data"); + free(buf); + return -1; + } + free(buf); + } + + closedir(root); + + return 0; +} + +int handle_path(int fd, const char *path) { const char *data; if (strcmp(path, "") == 0 || strcmp(path, "/") == 0) { - data = res; - } else if (strcmp(path, "/entry1") == 0) { - data = res1; + if (print_indexfile(fd, "index.gph") == -1) { + logmsg(LOG_INFO, "failed to send data"); + return -1; + } + } else if (strcmp(path, "/list") == 0) { + if (print_dir(fd, GOPHERROOT) == -1) { + logmsg(LOG_INFO, "failed to send data"); + return -1; + } } else { data = ""; + if (sendall(fd, data, strlen(data)) == -1) { + logmsg(LOG_INFO, "failed to send data"); + return -1; + } } - if (sendall(fd, data, strlen(data)) == -1) { - logmsg(LOG_INFO, "failed to send data"); - return -1; - } return 0; } diff --git a/gopher_client/client.c b/gopher_client/client.c new file mode 100644 index 0000000..5f6c23c --- /dev/null +++ b/gopher_client/client.c @@ -0,0 +1,89 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#include <arpa/inet.h> + +#define PORT "3000" // the port client will be connecting to + +#define MAXDATASIZE 100 // max number of bytes we can get at once + +// get sockaddr, IPv4 or IPv6: +void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +int main(int argc, char *argv[]) +{ + int sockfd, numbytes; + char buf[MAXDATASIZE]; + struct addrinfo hints, *servinfo, *p; + int rv; + char s[INET6_ADDRSTRLEN]; + + if (argc != 2) { + fprintf(stderr,"usage: client hostname\n"); + exit(1); + } + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return 1; + } + + // loop through all the results and connect to the first we can + for(p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("client: socket"); + continue; + } + + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("client: connect"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "client: failed to connect\n"); + return 2; + } + + inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), + s, sizeof s); + printf("client: connecting to %s\n", s); + + freeaddrinfo(servinfo); // all done with this structure + + if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) { + perror("recv"); + exit(1); + } + + buf[numbytes] = '\0'; + + printf("client: received '%s'\n",buf); + + close(sockfd); + + return 0; +} @@ -0,0 +1,288 @@ +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +typedef struct Elems Elems; +struct Elems { + char **e; + int num; +}; + +typedef struct Indexs Indexs; +struct Indexs { + Elems **n; + int num; +}; + + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) { + perror("calloc"); + exit(1); + } + + return p; +} + +void * +xmalloc(size_t size) +{ + void *p; + + if (!(p = malloc(size))) { + perror("malloc"); + exit(1); + } + + return p; +} + +void * +xrealloc(void *ptr, size_t size) +{ + if (!(ptr = realloc(ptr, size))) { + perror("realloc"); + exit(1); + } + + return ptr; +} + +char * +xstrdup(const char *str) +{ + char *ret; + + if (!(ret = strdup(str))) { + perror("strdup"); + exit(1); + } + + return ret; +} + +void +freeelem(Elems *e) +{ + if (e != NULL) { + if (e->e != NULL) { + for (;e->num > 0; e->num--) + if (e->e[e->num - 1] != NULL) + free(e->e[e->num - 1]); + free(e->e); + } + free(e); + } + return; +} + +void +freeindex(Indexs *i) +{ + if (i != NULL) { + if (i->n != NULL) { + for (;i->num > 0; i->num--) + freeelem(i->n[i->num - 1]); + free(i->n); + } + free(i); + } + + return; +} + +void +addelem(Elems *e, char *s) +{ + e->num++; + e->e = xrealloc(e->e, sizeof(char *) * e->num); + e->e[e->num - 1] = xstrdup(s); + + return; +} + +Elems * +getadv(char *str) +{ + char *b, *e, *o, *bo; + Elems *ret; + + ret = xcalloc(1, sizeof(Elems)); + + if (strchr(str, '\t')) { + addelem(ret, "i"); + addelem(ret, "Happy helping ☃ here: You tried to " + "output a spurious tab character. This will " + "break gopher. Please review your scripts. " + "Have a nice day!"); + addelem(ret, "Err"); + addelem(ret, "server"); + addelem(ret, "port"); + + return ret; + } + + if (str[0] == '[') { + o = xstrdup(str); + b = o + 1; + bo = b; + while ((e = strchr(bo, '|')) != NULL) { + if (e != bo && e[-1] == '\\') { + memmove(&e[-1], e, strlen(e)); + bo = e; + continue; + } + *e = '\0'; + e++; + addelem(ret, b); + b = e; + bo = b; + } + + e = strchr(b, ']'); + if (e != NULL) { + *e = '\0'; + addelem(ret, b); + } + free(o); + + if (ret->e != NULL && ret->e[0] != NULL && ret->e[0][0] == '\0') { + freeelem(ret); + ret = xcalloc(1, sizeof(Elems)); + + addelem(ret, "i"); + addelem(ret, "Happy helping ☃ here: You did not " + "specify an item type on this line. Please " + "review your scripts. " + "Have a nice day!"); + addelem(ret, "Err"); + addelem(ret, "server"); + addelem(ret, "port"); + + return ret; + } + + if (ret->e != NULL && ret->num == 5) + return ret; + + /* Invalid entry: Give back the whole line. */ + freeelem(ret); + ret = xcalloc(1, sizeof(Elems)); + } + + b = str; + if (*str == 't') + b++; + addelem(ret, "i"); + addelem(ret, b); + addelem(ret, "Err"); + addelem(ret, "server"); + addelem(ret, "port"); + + return ret; +} + +void +addindexs(Indexs *idx, Elems *el) +{ + idx->num++; + idx->n = xrealloc(idx->n, sizeof(Elems) * idx->num); + idx->n[idx->num - 1] = el; + + return; +} + +Indexs * +scanfile(char *fname) +{ + char *ln = NULL; + size_t linesiz = 0; + ssize_t n; + FILE *fp; + Indexs *ret; + Elems *el; + + if (!(fp = fopen(fname, "r"))) + return NULL; + + ret = xcalloc(1, sizeof(Indexs)); + + while ((n = getline(&ln, &linesiz, fp)) > 0) { + if (ln[n - 1] == '\n') + ln[--n] = '\0'; + el = getadv(ln); + if(el == NULL) + continue; + + addindexs(ret, el); + } + if (ferror(fp)) + perror("getline"); + free(ln); + fclose(fp); + + if (ret->n == NULL) { + free(ret); + return NULL; + } + + return ret; +} + +int +printelem(int fd, Elems *el, char *file, char *base, char *addr, char *port) +{ + char *path, *p, buf[PATH_MAX+1]; + int len; + + if (!strcmp(el->e[3], "server")) { + free(el->e[3]); + el->e[3] = xstrdup(addr); + } + if (!strcmp(el->e[4], "port")) { + free(el->e[4]); + el->e[4] = xstrdup(port); + } + + /* + * Ignore if the path is from base, if it might be some h type with + * some URL and ignore various types that have different semantics but + * to point to some file or directory. + */ + if ((el->e[2][0] != '\0' + && el->e[2][0] != '/' + && el->e[0][0] != 'i' + && el->e[0][0] != '2' + && el->e[0][0] != '3' + && el->e[0][0] != '8' + && el->e[0][0] != 'w' + && el->e[0][0] != 'T') && + !(el->e[0][0] == 'h' && !strncmp(el->e[2], "URL:", 4))) { + path = file + strlen(base); + if ((p = strrchr(path, '/'))) + len = p - path; + else + len = strlen(path); + snprintf(buf, sizeof(buf), "%s%.*s/%s", base, len, path, el->e[2]); + + if ((path = realpath(buf, NULL)) && + !strncmp(base, path, strlen(base))) { + p = path + strlen(base); + free(el->e[2]); + el->e[2] = xstrdup(p[0]? p : "/"); + } + free(path); + } + + if (dprintf(fd, "%.1s%s\t%s\t%s\t%s\r\n", el->e[0], el->e[1], el->e[2], + el->e[3], el->e[4]) < 0) { + perror("printelem: dprintf"); + return -1; + } + return 0; +} diff --git a/index.gph b/index.gph new file mode 100644 index 0000000..5942955 --- /dev/null +++ b/index.gph @@ -0,0 +1,4 @@ +0SubEntry 1 /entry1 localhost 3000
+0SubEntry 2 /entry2 localhost 3000
+0SubEntry 3 /entry3 localhost 3000
+.
@@ -1,5 +1,3 @@ -#define _POSIX_C_SOURCE 200112L - #include <time.h> #include <errno.h> #include <stdio.h> @@ -1,5 +1,3 @@ -#define _POSIX_C_SOURCE 200112L - #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/types.h> @@ -41,6 +39,7 @@ create_server(const char *port) perror("getaddrinfo"); return -1; } + for (p = res; p != NULL; p = p->ai_next) { if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { @@ -94,9 +93,9 @@ accept_connection(int s_fd) struct sockaddr_storage sa; socklen_t salen; char host[HOSTMAX]; - int c_fd; + int cfd; - if ((c_fd = accept(s_fd, (struct sockaddr *) & sa, &salen)) == -1) { + if ((cfd = accept(s_fd, (struct sockaddr *) & sa, &salen)) == -1) { if (errno != EWOULDBLOCK || errno != EAGAIN) { perror("accept"); exit(1); @@ -110,17 +109,17 @@ accept_connection(int s_fd) } logmsg(LOG_INFO, "connection accepted from %s", host); - return c_fd; + return cfd; } static void -handle_client(int c_fd) +handle_client(int cfd) { int n; char buf[BUFSIZE]; char *lf; - n = recv(c_fd, &buf, BUFSIZE - 1, 0); + n = recv(cfd, &buf, BUFSIZE - 1, 0); switch (n) { case -1: if (errno != EWOULDBLOCK || errno != EAGAIN) { @@ -139,20 +138,20 @@ handle_client(int c_fd) *lf = '\0'; logmsg(LOG_INFO, "path: %s", buf); - handle_path(c_fd, buf); + handle_path(cfd, buf); } - close(c_fd); + close(cfd); } static void -main_loop(int s_fd) +main_loop(int sfd) { int i, rc, end_server = 0; - int c_fd, nfds = 1, curr_size = 0; + int cfd, nfds = 1, curr_size = 0; struct pollfd pfds[OPEN_MAX]; memset(pfds, 0, sizeof(pfds)); - pfds[0].fd = s_fd; + pfds[0].fd = sfd; pfds[0].events = POLLIN; do { @@ -175,15 +174,15 @@ main_loop(int s_fd) end_server = 1; break; } - if (pfds[i].fd == s_fd) { + if (pfds[i].fd == sfd) { do { - c_fd = accept_connection(s_fd); - if (c_fd != -1) { - pfds[nfds].fd = c_fd; + cfd = accept_connection(sfd); + if (cfd != -1) { + pfds[nfds].fd = cfd; pfds[nfds].events = POLLIN; nfds++; } - } while (c_fd != -1); + } while (cfd != -1); } else { handle_client(pfds[i].fd); pfds[i].fd = -1; |