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; | 
