#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <sys/wait.h>


#include "smbmanager.h"
#include "fakedata.h"

extern void SMBManager_Destruct(SMBManager *pSMBManager) 
{
	free(pSMBManager->sharemsg);
	free(pSMBManager->smb_server_response);
}

extern void SMBManager_Construct(SMBManager *pSMBManager) {
	FILE *fp;
	int i=0, index=0;
	int size=0;
	int yes=1;
	U16 size_16;

	pSMBManager->i = 1;
	pSMBManager->num_shares = -1;
	pSMBManager->sharemsg_size = 0;
	pSMBManager->sharemsg = NULL;

	//data structure initialization
	
	pSMBManager->pdu_nbheader.type = 0x0;
	pSMBManager->pdu_smbheader.smbcmd = 0x0;	

	
	//read share file
	fp = fopen("/etc/fakesmb.dat", "r");
	i=0;
	while (!feof(fp)) {
		if (!i++) {
			fscanf(fp, "%s", pSMBManager->comment);
		}
		else {
		        pSMBManager->num_shares++;
		        fscanf(fp, "%s", pSMBManager->list[pSMBManager->num_shares]);
		}
	}
	fclose(fp);

	for (i=0; i<pSMBManager->num_shares; i++) {
		if (strlen(pSMBManager->list[i])  % 2 == 0) {
			size = size + 12 + 2*(strlen(pSMBManager->list[i])+2) + 16;
		}
		else {
			size = size + 12 + 2*(strlen(pSMBManager->list[i])+1) + 16;
		}

	}

	pSMBManager->sharemsg_size = sizeof(smb_25response_share_part1) + sizeof(smb_25response_share_part4);
	pSMBManager->sharemsg_size = pSMBManager->sharemsg_size + (12 * pSMBManager->num_shares) + (2 * 12) + size;
	//
        memset(pSMBManager->buf, 0, sizeof(pSMBManager->buf));   //zero out our read buffer

	//Read the comment
	
	smb_25response_server[sizeof(smb_25response_server) - 4] =  strlen(pSMBManager->comment)+1;
	smb_25response_server[sizeof(smb_25response_server) - 12] = strlen(pSMBManager->comment)+1;

	pSMBManager->smb_server_response_size =  2 * (1 + strlen(pSMBManager->comment)) + sizeof(smb_25response_server) + 4;

	pSMBManager->smb_server_response = (U8 *) calloc (pSMBManager->smb_server_response_size, sizeof(U8));

	memcpy (pSMBManager->smb_server_response, smb_25response_server, sizeof(smb_25response_server));

	index = sizeof(smb_25response_server);


	for (i=0; i < strlen(pSMBManager->comment); i++) {
		 pSMBManager->smb_server_response[index] = pSMBManager->comment[i];
		 index = index + 2;
	}

	size_16 = htons((U16) (pSMBManager->smb_server_response_size - 4));

        memcpy (&pSMBManager->smb_server_response[2], &size_16, sizeof(U16));


	// SET UP THE SOCKET
	//
	
	if ((pSMBManager->sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}

	if (setsockopt(pSMBManager->sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
		perror("setsockopt");
		exit(1);
	}


        pSMBManager->my_addr.sin_family = AF_INET;	// host byte order
        pSMBManager->my_addr.sin_port = htons(SMBPORT);	// short, network byte order
        pSMBManager->my_addr.sin_addr.s_addr = INADDR_ANY;	// automatically fill with my IP
        memset(&(pSMBManager->my_addr.sin_zero), '\0', 8);	// zero the rest of the struct


	pSMBManager->smb_status = INITIALIZED;

}

extern void SMBManager_ListenSocket(SMBManager *pSMBManager)
{
	if(bind(pSMBManager->sockfd, (struct sockaddr *)  &(pSMBManager->my_addr), sizeof(struct sockaddr)) 
			== -1) {

		perror("bind");
		exit(1);
	}

	if(listen(pSMBManager->sockfd, BACKLOG) == -1)  {
		perror("listen");
		exit(1);
	}

	pSMBManager->smb_status = WAITING_FOR_CONNECTION;

}

extern void SMBManager_Accept(SMBManager *pSMBManager)
{
	int sin_size = sizeof(struct sockaddr_in);

	if ((pSMBManager->newfd = accept(pSMBManager->sockfd, (struct sockaddr *) &(pSMBManager->their_addr),
					&sin_size)) == -1) {
		perror("accept");
	}

	pSMBManager->smb_status = GOT_CONNECTION;
#ifdef DEBUG
	printf("server: got connection from %s\n", inet_ntoa(pSMBManager->their_addr.sin_addr));
#endif
}



extern void SMBManager_ReadNBHeader(SMBManager *pSMBManager)
{
	pSMBManager->pdu_nbheader.type = 0x0;

	if (recv(pSMBManager->newfd, (void *) &(pSMBManager->pdu_nbheader), sizeof(nbheader), 0) == -1)  
		perror ("recv nbheader");

#ifdef DEBUG
	printf("nbheader: type=%02x length=%04x\n", pSMBManager->pdu_nbheader.type, ntohs(pSMBManager->pdu_nbheader.length));
#endif


		
}

extern void SMBManager_ReadSMBHeader(SMBManager *pSMBManager)
{
	pSMBManager->pdu_smbheader.smbcmd = 0x0;
	if (recv (pSMBManager->newfd, (void *) &pSMBManager->pdu_smbheader, sizeof(smbheader), 0) == -1)
		perror("recv smbheader");

#ifdef DEBUG
	printf ("smbheader: servercomp=%08x  smbcmd=%02x  ntstatus=%08x flags=%02x\n", ntohl(pSMBManager->pdu_smbheader.servercomp), pSMBManager->pdu_smbheader.smbcmd, ntohl(pSMBManager->pdu_smbheader.ntstatus), pSMBManager->pdu_smbheader.flags);
#endif

}

extern void SMBManager_CreateShareMsg(SMBManager *pSMBManager)
{
    U8 *sharemsg = NULL;
    int i=0, j=0;
    U8 dummy_share_data[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 };
    U8 dummy_IPC_ADMIN_data[] = { 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 };
    U8 dummy_comment_odd[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    U8 dummy_comment_even[] = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    U8 *sharedata_part2 = NULL;
    int sharedata_part2_size = 0;
    int sharedata_part2_index = 0;

    U8 *sharedata_part3 = NULL;
    int sharedata_part3_size = 0;

    U32 size_32;
    U16 size_16;


    U8 *scratch = NULL;
    int scratch_size; 
    int scratch_index;
    U32 sharesize;


    sharemsg = (U8 *) malloc(pSMBManager->sharemsg_size);

#ifdef DEBUG
    printf("SHAREMSG Size = %d\n",pSMBManager->sharemsg_size);
#endif

    sharedata_part2_size = (pSMBManager->num_shares * 12) + (2 * 12);
#ifdef DEBUG
    printf("sharedata_part2_size = %d\n",sharedata_part2_size);
#endif
    sharedata_part2 = (U8 *) malloc ( sharedata_part2_size );
 
    // iterate through each 
    // share writing out the 12 byte dummy data 
    // for each one plus another 24 bytes for the IPC and ADMIN shares
    for (i=0; i<pSMBManager->num_shares; i++) { 
	    sharedata_part2_index = i * sizeof(dummy_share_data);
	    memcpy(&sharedata_part2[sharedata_part2_index], dummy_share_data, sizeof(dummy_share_data));
    }

    sharedata_part2_index = sharedata_part2_index + sizeof(dummy_share_data);
    memcpy(&sharedata_part2[sharedata_part2_index], dummy_IPC_ADMIN_data, sizeof(dummy_share_data));
    sharedata_part2_index = sharedata_part2_index + sizeof(dummy_IPC_ADMIN_data);
    memcpy(&sharedata_part2[sharedata_part2_index], dummy_IPC_ADMIN_data, sizeof(dummy_IPC_ADMIN_data));

#ifdef DEBUG
    printf("Wrote out %d bytes to sharedata_part2\n", sharedata_part2_index); 
#endif
    
    
     

    // next we iterate through each share, 
    // writing the share name length and the share name

    for (i=0 ; i<pSMBManager->num_shares; i++) {
	    
	    sharesize = strlen(pSMBManager->list[i]) + 1;
	    if (strlen(pSMBManager->list[i]) % 2 == 0)  {
		    scratch_size = 12 + (sharesize*2) + 18;
	    }
	    else {
	    	    scratch_size = 12 + (sharesize*2) + 16;  //name length + unicode name + comment
	    }
	    scratch = calloc ( scratch_size, sizeof(U8) );

	    //first we copy the share name length into the buffer
	    memcpy(&scratch[0],&sharesize, sizeof(sharesize));
	    // skip the comment  (4 bytes all null)
	    memcpy(&scratch[8],&sharesize, sizeof(sharesize));

	    scratch_index = 12;

	    for (j=0; j < sharesize; j++)  {   //write out the sharename as unicode
		    scratch[scratch_index] = pSMBManager->list[i][j];
		    scratch_index = scratch_index + 2;
	    }


	    //write out the comment
            if ( strlen(pSMBManager->list[i])%2 == 0) {
	           memcpy(&scratch[scratch_index], dummy_comment_even, sizeof(dummy_comment_even));
	    }
	    else {
		   memcpy(&scratch[scratch_index], dummy_comment_odd, sizeof(dummy_comment_odd));
	    }


	    sharedata_part3 = (U8 *) realloc(sharedata_part3, sharedata_part3_size + scratch_size);  //allocate more buffer for the scratch daa
	    memcpy (&sharedata_part3[sharedata_part3_size], scratch, scratch_size); // copy the scratch buffer into the main buffer
	    sharedata_part3_size = sharedata_part3_size + scratch_size;  
	    free(scratch);
   
    }

    // last we join our four parts to make the complete sharemsg buffer
    //copy 1
    memcpy (sharemsg, smb_25response_share_part1, sizeof(smb_25response_share_part1));
    //copy 2 
    memcpy (&sharemsg[sizeof(smb_25response_share_part1)], sharedata_part2, sharedata_part2_size);
    //copy 3
    memcpy (&sharemsg[sizeof(smb_25response_share_part1) + sharedata_part2_size], sharedata_part3, sharedata_part3_size);
    //copy 4
    memcpy (&sharemsg[sizeof(smb_25response_share_part1) + sharedata_part2_size + sharedata_part3_size], smb_25response_share_part4, sizeof(smb_25response_share_part4));
    free(sharedata_part2);
    free(sharedata_part3);
    

    size_16 = htons((U16) (pSMBManager->sharemsg_size - 4));
    memcpy (&sharemsg[2], &size_16, sizeof(U16));

    size_16 = (U16) (pSMBManager->sharemsg_size - 60);
    memcpy (&sharemsg[39], &size_16, sizeof(U16));
    memcpy (&sharemsg[49], &size_16, sizeof(U16));
    memcpy (&sharemsg[68], &size_16, sizeof(U16));
    size_16 = size_16 + 1;
    memcpy (&sharemsg[57], &size_16, sizeof(U16));
    size_32 = (U32) (pSMBManager->num_shares + 2);
#ifdef DEBUG
    printf ("number of shares = %d",(pSMBManager->num_shares+2));
#endif
    memcpy (&sharemsg[96], &size_32, sizeof(U32));
    memcpy (&sharemsg[104],&size_32, sizeof(U32));

 


    pSMBManager->sharemsg = sharemsg;


}

extern void SMBManager_ProcessNBRequest (SMBManager *pSMBManager)
{
	U8 buf[1024];
	memset(buf, 0, sizeof(buf));

	if (recv (pSMBManager->newfd, (void *) buf, ntohs(pSMBManager->pdu_nbheader.length), 0) == -1)
    		perror("recv nb sess request");
#ifdef DEBUG
	printf("Got the nb session request\n");
#endif

	if (send (pSMBManager->newfd, (void *) nb_81response, sizeof(nb_81response), 0) == -1)
    		perror("send nb session resp");
#ifdef DEBUG
	printf("Sent the nb session response -  %ld bytes\n", sizeof(nb_81response));
#endif
}


extern void SMBManager_ProcessSMBRequest (SMBManager *pSMBManager) 
{
	U8 buf[1024];
	int i=0;
	memset(buf, 0, sizeof(buf));
        if (recv (pSMBManager->newfd, (void *) buf, ntohs(pSMBManager->pdu_nbheader.length) - sizeof(smbheader), 0) == -1)
		perror("recv smb message");

	switch (pSMBManager->pdu_smbheader.smbcmd) {
		
		case 0x71:      //tree disconnect request 0x71
#ifdef DEBUG
		    printf("Got the Tree Disconnect Request\n");
#endif
		    memcpy(&smb_71response[16], &pSMBManager->pdu_smbheader.other_crap, 20);
		    if (send (pSMBManager->newfd, (void *) smb_71response, sizeof(smb_71response), 0) == -1)
			perror("send npreply");
#ifdef DEBUG
		    printf ("Sent the tree disconnect Response -  %ld bytes\n", sizeof(smb_71response));
#endif

		    break;

		case 0x72:	//Negotiate Protocol Request 0x72

#ifdef DEBUG
		    printf("Got the Negotiate Protocol Request\n");
#endif
		    memcpy(&smb_72response[16], &pSMBManager->pdu_smbheader.other_crap, 20);
		    if (send (pSMBManager->newfd, (void *) smb_72response, sizeof(smb_72response), 0) == -1)
			perror("send npreply");
#ifdef DEBUG 
		    printf ("Sent the Negotiate Protocol Response -  %ld bytes\n", sizeof(smb_72response));
#endif

		    break;

		case 0x73:	//Session Startup 0x73
 
#ifdef DEBUG
		    printf("Got Session startup\n");
#endif
		    memcpy(&smb_73response[16], &pSMBManager->pdu_smbheader.other_crap, 20);
		    if (send (pSMBManager->newfd, (void *) smb_73response, sizeof(smb_73response), 0) == -1)
			perror("send NB session");
#ifdef DEBUG
		    printf("Sent the NB Session Response -  %ld bytes\n", sizeof(smb_73response));
#endif

		    break;

		case 0x2f:	//0x2f
 
#ifdef DEBUG
		    printf("Got Session startup\n");
#endif
		    memcpy(&smb_2fresponse[16], &pSMBManager->pdu_smbheader.other_crap, 20);
		    if (send (pSMBManager->newfd, (void *) smb_2fresponse, sizeof(smb_2fresponse), 0) == -1)
			perror("send NB session");
#ifdef DEBUG 
		    printf("Sent the NB Session Response -  %ld bytes\n", sizeof(smb_2fresponse));
#endif

		    break;

		case 0x2e:	//0x2e
 
#ifdef DEBUG
		    printf("Got Session startup\n");
#endif
		    memcpy(&smb_2eresponse[16], &pSMBManager->pdu_smbheader.other_crap, 20);
		    if (send (pSMBManager->newfd, (void *) smb_2eresponse, sizeof(smb_2eresponse), 0) == -1)
			perror("send NB session");
#ifdef DEBUG 
		    printf("Sent the NB Session Response -  %ld bytes\n", sizeof(smb_2eresponse));
#endif

		    break;


		case 0x25:	//NetShareEnum Request

#ifdef DEBUG
		    printf("Got NetShareEnum - type = %02x\n", buf[54]);
#endif

		    if (buf[54] == 0x0b) {	//type is bind

			memcpy(&smb_25responsebind[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			if (send (pSMBManager->newfd, (void *) smb_25responsebind, sizeof(smb_25responsebind), 0) == -1)
			    perror("send netshare responsebind");
#ifdef DEBUG
			printf ("Sent the NetShareEnum Response, bind -  %ld bytes\n", sizeof(smb_25responsebind));
#endif
		    }

		    else if (buf[54] == 0x00) {	//type is Request
			if (buf[74] == 0x0f && buf[75] == 0x00) {	//opnum is share
 
#ifdef DEBUG
			    printf ("NetShareEnum type is 'request', opnum - %02x%02x\n", buf[74], buf[75]);
#endif
			    memcpy(&pSMBManager->sharemsg[16],  &pSMBManager->pdu_smbheader.other_crap, 20); //NEWBUF

			    if (send (pSMBManager->newfd, (void *) pSMBManager->sharemsg, pSMBManager->sharemsg_size, 0) == -1)  
				perror("send netshare response_share");
#ifdef DEBUG 
			    printf ("Sent the NetShareEnum Response (share) -  %d bytes\n", pSMBManager->sharemsg_size);    
#endif
			} 
			
			
			else if (buf[74] == 0x00 && buf[75] == 0x00) {	//opnum is wksta (workstation?)
#ifdef DEBUG
			    printf ("NetShareEnum type is 'request', opnum - %02x%02x\n", buf[74], buf[75]);
#endif
			    memcpy(&smb_25response_wksta[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			    if (send (pSMBManager->newfd, (void *) smb_25response_wksta, sizeof(smb_25response_wksta), 0) == -1)
				perror("send netshare response_wksta");
#ifdef DEBUG
			    printf ("Sent the NetShareEnum Response (workstation) -  %ld bytes\n", sizeof(smb_25response_wksta));
#endif
			} 

		 	else if (buf[74] == 0x15 && buf[75] == 0x00) {	//opnum is server
#ifdef DEBUG
			    printf ("NetShareEnum type is 'server', opnum - %02x%02x\n", buf[74], buf[75]);
#endif
			    memcpy(&pSMBManager->smb_server_response[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			    if (send (pSMBManager->newfd, (void *) pSMBManager->smb_server_response, pSMBManager->smb_server_response_size, 0) == -1)
				perror("send netshare response_server");
#ifdef DEBUG
			    printf ("Sent the NetShareEnum Response (workstation) -  %ld bytes\n", sizeof(pSMBManager->smb_server_response_size));
#endif
			}	
			
			else if (buf[74] == 0x02 && buf[75] == 0x00) {        //opnum is openHKLM
#ifdef DEBUG
				printf("WINREG - OpenHKLM request, opnum - %02x%02x\n",  buf[74], buf[75]);
#endif
			    memcpy(&smb_25response_hklm[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			    if (send (pSMBManager->newfd, (void *) smb_25response_hklm, sizeof(smb_25response_hklm), 0) == -1)
				perror("send openHKLM response");
#ifdef DEBUG
			    printf ("Sent the openHKLM response -  %ld bytes\n", sizeof(smb_25response_hklm));
#endif
			} 
			
			
			else if (buf[74] == 0x0f && buf[75] == 0x00) {        //opnum is openkey request
#ifdef DEBUG
				printf("WINREG - OpenKey request, opnum - %02x%02x\n",  buf[74], buf[75]);
#endif
			    memcpy(&smb_25response_key[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			    if (send (pSMBManager->newfd, (void *) smb_25response_key, sizeof(smb_25response_key), 0) == -1)
				perror("send openkey response");
#ifdef DEBUG
			    printf ("Sent the openkey response -  %ld bytes\n", sizeof(smb_25response_key));
#endif
			} 
			
			
			else if (buf[74] == 0x05 && buf[75] == 0x00) {        //opnum is closekey request
#ifdef DEBUG
				printf("WINREG - closeKey request, opnum - %02x%02x\n",  buf[74], buf[75]);
#endif
			    memcpy(&smb_25response_close[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			    if (send (pSMBManager->newfd, (void *) smb_25response_close, sizeof(smb_25response_close), 0) == -1)
				perror("send closekey response");
#ifdef DEBUG
			    printf ("Sent the closekey response -  %ld bytes\n", sizeof(smb_25response_close));
#endif
			} 
			
			
			else if (buf[74] == 0x45 && buf[75] == 0x00) {        //opnum is openprinter request
#ifdef DEBUG
				printf("WINREG - openPrinter request, opnum - %02x%02x\n",  buf[74], buf[75]);
#endif
			    memcpy(&smb_25response_openprinter[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			    if (send (pSMBManager->newfd, (void *) smb_25response_openprinter, sizeof(smb_25response_openprinter), 0) == -1)
				perror("send openPrinter response");
#ifdef DEBUG
			    printf ("Sent the openPrinter response -  %ld bytes\n", sizeof(smb_25response_openprinter));
#endif
			} 
			
			
			else if (buf[74] == 0x1d && buf[75] == 0x00) {        //opnum is closeprinter request
#ifdef DEBUG
				printf("WINREG - closePrinter request, opnum - %02x%02x\n",  buf[74], buf[75]);
#endif
			    memcpy(&smb_25response_closeprinter[16], &pSMBManager->pdu_smbheader.other_crap, 20);
			    if (send (pSMBManager->newfd, (void *) smb_25response_closeprinter, sizeof(smb_25response_closeprinter), 0) == -1)
				perror("send closePrinter response");
#ifdef DEBUG
			    printf ("Sent the closePrinter response -  %ld bytes\n", sizeof(smb_25response_closeprinter));
#endif
			}

			
			else {
#ifdef DEBUG
			    printf ("ERROR - Invalid opnum in NetShareEnum request - %02x %02x\n", buf[74], buf[75]);
#endif
			}

		    }

		    else {
#ifdef DEBUG
			printf ("ERROR - Invalid type in NetShareEnum Request\n");
#endif
		    }


		    break;

		case 0xa2:	//NT create AndX Request

#ifdef DEBUG
		    printf("NT create AndX Request\n");
#endif

		    smb_a2response[42]++;
		    memcpy(&smb_a2response[16], &pSMBManager->pdu_smbheader.other_crap,
			   20);

		    if (send
			(pSMBManager->newfd, (void *) smb_a2response,
			 sizeof(smb_a2response), 0) == -1)
			perror("send NT create AndX Response");
#ifdef DEBUG
		    printf ("Sent the NT create AndX Response -  %ld bytes\n", sizeof(smb_a2response));
#endif
		    break;

		case 0x04:	//Close request

#ifdef DEBUG
		    printf("Close Request\n");
#endif
		    memcpy(&smb_04response[16], &pSMBManager->pdu_smbheader.other_crap,
			   20);
		    if (send
			(pSMBManager->newfd, (void *) smb_04response,
			 sizeof(smb_04response), 0) == -1)
			perror("sent Close Response");
#ifdef DEBUG
		    printf("Sent the Close Response -  %ld bytes\n", sizeof(smb_04response));
#endif
		    break;


		default:
#ifdef DEBUG
		    printf("WARNING: Unknown SMB protocol type!\n");
#endif
		    SMBManager_Disconnect(pSMBManager);
		} //end switch for smb message


}

extern void SMBManager_Disconnect(SMBManager *pSMBManager)
{
	close(pSMBManager->newfd);
	pSMBManager->smb_status = DISCONNECTED;
}


