//From Brooklyn Polytechnic CTF 2005

/*
** server.c -- a stream socket server demo
Basic Server code from 
Beej's Guide to Network Programming:
http://beej.us/guide/bgnet/output/htmlsingle/bgnet.html#simpleserver

It was changed to be more modular and the stage functions were added.
-Stanislav Nurilov
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "send_flag.h"

#define MYPORT 3490    // the port users will be connecting to
#define BACKLOG 20     // how many pending connections queue will hold
#define LOSER_STRING "I don't know who you are, but you're a loser!\n"
#define PROMPT "step2> "
#define RECV_BYTES 1024
#define MAX_BUFF 1024

#ifndef SEND_MACRO
#define SEND_MACRO(f, s) if(send(f, s, strlen(s),0)==-1) perror("send")
#endif

void client_function(int fd);
void server_function(struct sockaddr_in *their_addr);
int cl_authenticate(int fd);
int exploit_me(int fd);

char dude1[256];
char dude2[256];
char dude3[256];
char dude4[256];

void sigchld_handler(int s)
{
    while(waitpid(-1, NULL, WNOHANG) > 0);
}

int start_server_listen(int port, int backlog){
	int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
	struct sockaddr_in my_addr;    // my address information
	struct sockaddr_in their_addr; // connector's address information
	socklen_t sin_size;
	struct sigaction sa;
	int yes=1;

	if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}

	if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
		perror("setsockopt");
		exit(1);
    }

	my_addr.sin_family = AF_INET;         // host byte order
	my_addr.sin_port = htons(port);     // short, network byte order
	my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
	memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct

	if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1) {
		perror("bind");
		exit(1);
	}

	if (listen(sockfd, backlog) == -1) {
		perror("listen");
		exit(1);
	}
	return sockfd;
}

int main(void)
{
	int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
	struct sockaddr_in my_addr;    // my address information
	struct sockaddr_in their_addr; // connector's address information
	socklen_t sin_size;
	struct sigaction sa;
	
	memset(dude1, 'A', 256);
	memset(dude2, 'S', 256);
	memset(dude3, 'V', 256);
	memset(dude4, 'N', 256);

	memcpy(dude3, "DEADBEEF", 8);
	memcpy(dude1,   "NOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOPNOP", 111);
	memcpy(dude2, "SHELLCODESHELLCODESHELLCODESHELLCODESHELLCODESHELLCODESHELLCODESHELLCODE", 72);
	memcpy(dude3+8, "STACKGUARDSTACKGUARDSTACKGUARDSTACKGUARDSTACKGUARD",50 );
	memcpy(dude4,   "ADDRESSADDRESSADDRESSADDRESSADDRESSADDRESSADDRESS",49);
	
	sockfd = start_server_listen(MYPORT, BACKLOG );

	sa.sa_handler = sigchld_handler; // reap all dead processes
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESTART;
	if (sigaction(SIGCHLD, &sa, NULL) == -1) {
		perror("sigaction");
		exit(1);
	}

	while(1) {  // main accept() loop
        	sin_size = sizeof(struct sockaddr_in);
		if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
			perror("accept");
			continue;
		}
		server_function(&their_addr);
		if (!fork()) { // this is the child process
			close(sockfd); // child doesn't need the listener
			client_function(new_fd);
			exit(0);
		}
		close(new_fd);  // parent doesn't need this
	}
	return 0;
}

void client_function(int new_fd){
	SEND_MACRO(new_fd, "Welcome to Stage 3, Step 2!\n");
	if(cl_authenticate(new_fd)==1){
		SEND_MACRO(new_fd, "Authenticated, now let's dance!\n");
		if(exploit_me(new_fd)==1){
			SEND_MACRO(new_fd, "\nGood Job!\nHere's your flag: \n");
			if(send_flag(new_fd)==0){
				SEND_MACRO(new_fd, "There was an error, which you must report to one of the organizers. MSG: ST3-2s\n");
			}
			SEND_MACRO(new_fd, "Next step at ");
			SEND_MACRO(new_fd, "http://128.238.66.71/step3/step3.php\n");
		}else{
			SEND_MACRO(new_fd, "\nWhat kind of a hacker are you? Go away and don't come back!\n");
		}
	}else{
		SEND_MACRO(new_fd, LOSER_STRING); 
	}
    close(new_fd);
}

void server_function(struct sockaddr_in *their_addr){
	FILE * f = fopen("/home/step2/connections.list", "a");
	if(f == NULL){
		printf("server: got connection from %s\n", inet_ntoa(their_addr->sin_addr));
	}else{
		fprintf(f, "server: got connection from %s\n", inet_ntoa(their_addr->sin_addr));
		fclose(f);
	}
}

int cl_authenticate(int fd){
	char buff[MAX_BUFF];
	int bytes_recv = 0;
	memset(buff, 0, MAX_BUFF);
	if(send(fd, PROMPT, strlen(PROMPT),0)==-1) perror("send");
	if((bytes_recv = recv(fd, buff, sizeof(char) * MAX_BUFF,0)) == -1) perror("recieve error");
	if(bytes_recv >1 || bytes_recv == 0){
		SEND_MACRO(fd, "\nYou're sending the wrong number of bytes!\n");
	}else{
		if(memcmp(buff, "1", 1)==0){
			SEND_MACRO(fd, "\nCongradulations on getting this far!\n");
			return(1);
		}else{
			SEND_MACRO(fd, "\nWhat can I tell you? You're not doing too well!\n");
			return(0);
		}
	}
	return(0);
}

int exploit_me(int fd){
	char buff4[256];
	char buff3[256];
	char buff2[256];
	char buff[256];
	int bytes_recv = 0;
	int i=0;

	memset(buff, 0, MAX_BUFF);
	memset(buff2, 'S', 256);
	memset(buff3, 'V', 256);
	memset(buff4, 'N', 256);

	SEND_MACRO(fd, PROMPT);
	if((bytes_recv = recv(fd, buff, sizeof(char) * MAX_BUFF -1,0)) == -1) perror("recieve error");
	if(bytes_recv < 256*3){
		SEND_MACRO(fd, "\nYou're sending the wrong number of bytes, again!\n");
	}

	if(memcmp(buff, dude1,256)==0){
		if(memcmp(buff2, dude2,256)==0){
			if(memcmp(buff3, dude3,256)==0){
				if(memcmp(buff4, dude4,256)==0){
					SEND_MACRO(fd, "\nYou finally got it. That's how overflows work.\n");
					return(1);
				}else{
					SEND_MACRO(fd, "\nSo close, yet so far.\n");
				}
			}else{
				SEND_MACRO(fd, "\nWow, you're getting better. You're not good enough though.\n");
			}
		}else{
			SEND_MACRO(fd, "\nCloser, but not better.\n");
		}
	}else{
		SEND_MACRO(fd, "\nYou'll have to do better than that.\n");
	}
	return 0;
}

