summaryrefslogblamecommitdiffstats
path: root/client.c
blob: e99ef09337d9faa26595d779330f92912a45cab8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12




                   
                    





                   
                                                  
                                                   
 
                                        


                                                                 
                                                       









                                                                                                                                                                              
                                        
                                               


                                    


                                                  


                                                           
                                   
                                                       

                                            





                                                
                                                                      






                                                                                                                               
                                           
                           
                                               







                                                 
                                                                                                







                                                                                           


                                                                
                                                         
                                                                                                                   
                                                    

                                       



                                                                                                
                                                         

                                                                          



                                                                                                                                 
                                                                                                                   

                                                    
                         
                 








                                                                                                                   
                                                             
                                                                                       











                                                                                                                            
         

 





                                                        
 
                                     
                                                          

                                                                  




                                                                                            
                                                                    
                                       
                                                                                                              
                                                    
                                             







                                                                                                 
                                                                
                                 
                                                                                                              

                                                            
                                                       
                         

                                     




                                                                                                                        
                                                                                                                                              

                                           
                                                                   

                                                                                            
                    

 
                                                                                         
 
                                                                  


                                                                                 
                                                                             

                                            
                 
                                                 





                                                                                

                                                                        
                                           
                                                                                                                           


                                            




                                                                                                                        
                         

                                                                                                                      


                                            
                                                                                                        
                                                                                                                                                                                    


                                                    




                                                                 

                                          

 








                                                                                                                         
#include "client.h"
#include "proxy.h"
#include "epoll.h"
#include "helper.h"
#include "asn1.h"
#include "openssl.h"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

static void client_haveIn(epoll_client_t *client);
static BOOL client_haveOut(epoll_client_t *client);

void client_free(epoll_client_t *client)
{
	proxy_removeClient(client);
	if (client->sendBuffer != NULL) free(client->sendBuffer);
	if (client->ssl != NULL) SSL_free(client->ssl);
	if (client->fd != -1) {
		//ePoll_remove(client->fd); // epoll would remove closed descriptors automatically, but just to be sure we don't cause use after free, we remove it explicitly
		close(client->fd);
	}
	free(client);
}

void client_callback(void *data, int haveIn, int haveOut, int doCleanup)
{
	epoll_client_t *client = (epoll_client_t*)data;
	if (doCleanup || client->kill) {
		//printf("Client gone (1).\n");
		client_free(client);
		return;
	}
	if (client->ssl == NULL) {
		// Plain connection
		if (haveIn) client_haveIn(client);
		if (haveOut || client->kill) {
			if (client_haveOut(client)) return;
		}
		if (client->kill) {
			//printf("Client gone (2).\n");
			client_free(client);
		}
		return;
	}
	// SSL connection
	if (!client->sslAccepted) {
		// Still SSL-Connecting
		if (!ssl_acceptClient(client)) {
			printf("[Proxy] SSL Client accept failed.\n");
			client_free(client);
			return;
		}
		if (!client->sslAccepted) return;
	}
	// Since we don't know if the incoming data is just wrapped application data or ssl protocol stuff, we always call both
	client_haveIn(client);
	if (client_haveOut(client)) return;
	if (client->kill) {
		//printf("Client gone (3).\n");
		client_free(client);
	}
}

static void client_haveIn(epoll_client_t *client)
{
	for (;;) {
		if (client->rbPos >= REQLEN) {
			printf("[Proxy] Buffer overflow reading from client. Disconnecting.\n");
			client->kill = TRUE;
			return;
		}
		const size_t buflen = REQLEN - client->rbPos;
		ssize_t ret;
		if (client->ssl == NULL) {
			// Plain
			ret = read(client->fd, client->readBuffer + client->rbPos, buflen);
			if (ret < 0 && errno == EINTR) continue;
			if (ret < 0 && errno == EAGAIN) break;
			if (ret <= 0) {
				if (client->kill) return;
				printf("[Proxy] Client gone while reading (ret=%d, errno=%d).\n", (int)ret, errno);
				client->kill = TRUE;
				return;
			}
		} else {
			// SSL
			ret = SSL_read(client->ssl, client->readBuffer + client->rbPos, buflen);
			if (ret <= 0) {
				if (client->kill) return;
				int err = SSL_get_error(client->ssl, ret);
				if (SSL_BLOCKED(err)) break;
				if (err == SSL_ERROR_ZERO_RETURN) {
					SSL_shutdown(client->ssl);
					// Might have to handle error return codes; need another state in epoll_client_t for this
				}
				printf("[Proxy] SSL client gone while reading (ret=%d, err=%d).\n", (int)ret, err);
				client->kill = TRUE;
				return;
			}
		}
		// We reach here if something was read (so ret > 0)
		client->rbPos += ret;
		// Request complete?
		for (;;) {
			size_t consumed, len;
			consumed = scan_asn1SEQUENCE(client->readBuffer, client->readBuffer + client->rbPos, &len);
			if (consumed == 0) break; // Length-Header not complete
			len += consumed;
			if (len > client->rbPos) break; // Body not complete yet
			if (!proxy_fromClient(client, len)) {
				printf("[Proxy] Error parsing request from client.\n");
				client->kill = TRUE;
				return;
			}
			// Shift remaining buffer contents
			if (len == client->rbPos) {
				client->rbPos = 0;
				break;
			}
			memmove(client->readBuffer, client->readBuffer + len, client->rbPos - len);
			client->rbPos -= len;
		}
		if (client->ssl == NULL && (ssize_t)buflen > ret) break; // Read less than buffer len, epoll will fire again
	}
}

/**
 * Send any queued data.
 * Returns TRUE if anything has been sent, or the socket
 * buffer is currently full (EAGAIN, non-blocking mode)
 */
static BOOL client_haveOut(epoll_client_t *client)
{
	client->writeBlocked = FALSE;
	if (client->sbPos >= client->sbFill) return FALSE;
	while (client->sbPos < client->sbFill) {
		const int tosend = client->sbFill - client->sbPos;
		ssize_t ret;
		if (client->ssl == NULL) {
			// Plain
			ret = write(client->fd, client->sendBuffer + client->sbPos, tosend);
			if (ret < 0 && errno == EINTR) continue;
			if (ret < 0 && errno == EAGAIN) return TRUE;
			if (ret <= 0) {
				printf("[Proxy] Cannot send to client (ret=%d, errno=%d)\n", (int)ret, errno);
				client->kill = TRUE;
				return FALSE;
			}
		} else {
			// SSL
			ret = SSL_write(client->ssl, client->sendBuffer + client->sbPos, tosend);
			if (ret <= 0) {
				int err = SSL_get_error(client->ssl, ret);
				if (SSL_BLOCKED(err)) {
					client->writeBlocked = TRUE;
					return TRUE; // Blocking
				}
				printf("[Proxy] SSL cannot send to client (ret=%d, err=%d)\n", (int)ret, err);
				ERR_print_errors_fp(stdout);
				client->kill = TRUE;
				return FALSE; // Closed
			}
		}
		client->sbPos += ret;
		if (client->ssl != NULL) {
			memmove(client->sendBuffer, client->sendBuffer + client->sbPos, client->sbFill - client->sbPos);
			client->sbFill -= client->sbPos;
			client->sbPos = 0;
		}
		if (client->ssl == NULL && ret != tosend) return TRUE; // Sent less than requested -> buffer full, epoll will trigger us again
	}
	client->sbPos = client->sbFill = 0;
	if (client->ssl == NULL && client->sbLen > MAXMSGLEN / 2) {
		helper_realloc(&client->sendBuffer, &client->sbLen, 8000, "client_haveOut");
	}
	return TRUE;
}

BOOL client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL cork)
{
	if (client->ssl == NULL && client->sbFill == 0 && !cork) {
		// Nothing in send buffer, fire away
		const int ret = write(client->fd, buffer, len);
		if (ret == 0 || (ret < 0 && errno != EINTR && errno != EAGAIN)) {
			printf("[Proxy] Client gone when trying to send.\n");
			client->kill = TRUE;
			return FALSE;
		}
		if (ret == (int)len) return TRUE;
		// Couldn't send everything, continue with buffering logic below
		if (ret > 0) {
			buffer += ret;
			len -= (size_t)ret;
		}
	}
	// Buffer...
	if (client->sbLen - client->sbFill < len) { // Buffer too small?
		if (client->writeBlocked) {
			printf("[Proxy] SSL write to client blocked and buffer to small (%d bytes)\n", (int)client->sbLen);
			client->kill = TRUE;
			return FALSE;
		}
		if (client->sbPos != 0) { // See if we can compact it
			memmove(client->sendBuffer, client->sendBuffer + client->sbPos, client->sbFill - client->sbPos);
			client->sbFill -= client->sbPos;
			client->sbPos = 0;
		}
		// Sanity
		if (client->sbFill + len > MAXMSGLEN) {
			printf("[Proxy] Dropping client as the send buffer would exceed %d bytes.\n", (int)MAXMSGLEN);
			client->kill = TRUE;
			return FALSE;
		}
		if (client->sbLen - client->sbFill < len) { // Still too small after compacting? realloc
			if (helper_realloc(&client->sendBuffer, &client->sbLen, client->sbLen + len + (client->ssl && client->sbLen > 0 ? MAXMSGLEN : 4000), "client_send") == -1) {
				client->kill = TRUE;
				return FALSE;
			}
		}
	}
	// Finally append to buffer
	memcpy(client->sendBuffer + client->sbFill, buffer, len);
	client->sbFill += len;
	if (!cork) client_haveOut(client);
	return TRUE;
}

BOOL client_searchResultError(epoll_client_t *client, const unsigned long messageId, const int code, const char *message)
{
	const size_t doneLen = fmt_ldapsearchresultdone(NULL, code, "", message, "");
	const size_t doneHeaderLen = fmt_ldapmessage(NULL, messageId, SearchResultDone, doneLen);
	char buffer[doneLen + doneHeaderLen];
	fmt_ldapsearchresultdone(buffer + doneHeaderLen, code, "", message, "");
	fmt_ldapmessage(buffer, messageId, SearchResultDone, doneLen);
	return client_send(client, buffer, doneHeaderLen + doneLen, FALSE);
}