I am trying to do a chatroom where I have a server and multi clients, but I have a problem, when I execute my code of server (serverCHAT.c), I have a problem with the bind connection. I don't know why.
When I execute the program, the if sentence of a bind problems appears on the console.
I check the connection, but I can not find the error.
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <stdio.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define MAX_CLIENT 10
/*
BIBLIOGRAFIA:
https://github.com/yorickdewid/Chat-Server
comando linux ip: ifconfig es : inet addr
*/
int totcltes=0;
int client_sockfd[MAX_CLIENT];
void *coneccion_clte(void *arg);
int clientes_conectados[MAX_CLIENT];
static unsigned int cli_count = 0;
static int uid = 10;
/* Cliente */
typedef struct {
struct sockaddr_in addr; /* Client remote address */
int connfd; /* Connection file descriptor */
int uid; /* Client unique identifier */
char name[32]; /* Client name */
} client_t;
client_t *clients[MAX_CLIENT];
/* agregar cliente a la cola */
void queue_add(client_t *cl){
int i;
for(i=0;i<MAX_CLIENT;i++){
if(!clients[i]){
clients[i] = cl;
return;
}// if
}// for
}// agregar cliente
/* quitar cliente de la cola */
void queue_delete(int uid){
int i;
for(i=0;i<MAX_CLIENT;i++){
if(clients[i]){
if(clients[i]->uid == uid){
clients[i] = NULL;
return;
}// if
}// if
}// for
}// quitar cola
/* Senviar mensaje */
void enviar_mensaje(char *s){
int i;
for(i=0;i<MAX_CLIENT;i++){
if(clients[i]){
write(clients[i]->connfd, s, strlen(s));
}// if
}// for
}// enviar mensaje
/* Handle all communication with the client */
void *coneccion_clte(void *arg){
char buff_out[1024];// mensaje de salida
char buff_in[1024]; // mensaje de entrada
int rlen; // longitu del mensaje
cli_count++; //aumentamos en uno el cliente
client_t *cli = (client_t *)arg;
printf("Cliente Aceptado %d ", cli->uid);
/* Recibiendo mensaje */
while((rlen = read(cli->connfd, buff_in, sizeof(buff_in)-1)) > 0){
buff_in[rlen] = '\0';
buff_out[0] = '\0';
/* Special options */
if(strncmp("exit",buff_in,4==0)){
break;
}else{
sprintf(buff_out, "[%s]: %s\n", cli->name, buff_in);
enviar_mensaje(buff_out);
}//else
}//while
/* Cerrar al conexion */
close(cli->connfd);
/* Delete client from queue and yeild thread */
queue_delete(cli->uid);
printf("Fin del chat :c ");
free(cli);
cli_count--;
//pthread_detach(pthread_self());
return NULL;
}//coneccion_clte
int main()
{
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
int server_sockfd ;// listenfd
//int listenfd = 0;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
//int parametro[MAX_CLIENT];
//pthread_t tid[MAX_CLIENT];
//int i;
pthread_t tid;
int connfd = 0; //para obtener el descriptor de archivo (connection file descriptor y saber que hilo es)
/* configuracion del socket*/
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
//listenfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
/*
bind()
Avisa al SO que hemos abierto un socket y asociamos nuestro programa a este socket
*/
//bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
if(bind(server_sockfd, (struct sockaddr *)&server_address,sizeof(serv_addr)) < 0);{
printf("Error: bind \n");
//return 1;
}//if bind
/*
Crear una cola de conexiones
Listen
Indicamos al programa que empiece a escuchar peticiones y las registre
*/
if(listen(server_sockfd, 5) < 0){
printf("Error: Listen");
//return 1;
}//is listen
printf("<[Servidor Inicializado :D]>\n");
/* Aceptar clientes */
while(totcltes<MAX_CLIENT){
connfd = accept(server_sockfd, (struct sockaddr*)&client_address, &client_len);
/* Revisar el Total de clientes */
if((cli_count+1) == MAX_CLIENT){
printf("Clientes Maximos Alcanzados \n");
close(connfd);
continue;// para que revise la siguiente iteracion
}// if
/* Configuracion del cliente */
client_t *cli = (client_t *)malloc(sizeof(client_t));// creamos la estructura cliente cli
cli->addr = client_address;
cli->connfd = connfd;
cli->uid = uid++;
sprintf(cli->name, "%d", cli->uid);
/* Agregar clientes a la cola */
queue_add(cli);
pthread_create(&tid, NULL, &coneccion_clte, (void*)cli);
}//while aceptar clientes
}//main
You are using sizeof(serv_addr) as the length specifier instead of server_len.
You are declaring server_len as an int: it should be socklen_t.
You are doing the same thing with client_len: it should be socklen_t.
Please go back and study the original code from githu. It actually works.
I want to implement an UDP server that works with three threads (over three port) that do the same thing. In one thread each there is a parent and a child: parent waiting for requests from the client, child increment a variable. When parent receive the request, it send a signal (SIGUSR1) to the pid of the child (to try, I use parentpid+1). Child by default increment the variable, by SIGUSR1 write something (for example: "I'm in sigurs1"), but actually I want also to send the status of the variable to the main thread. Howevere, this is the code:
/**
#defgroup Group4 UDP Client and Server
#brief UDP/IPv4 Client and Server
#{
*/
/**
#file UDPServer.c
#author Catiuscia Melle
#brief Presentazione di un UDP Echo Server IPv4.
Il server, in un ciclo infinito:
- riceve un messaggio
- lo re-invia in echo.
*/
#include "Header.h"
//### PRIMA DI TUTTO INCLUDIAMO LA LIBRERIA PER I SEGNALI
#include <signal.h>
void *thread_function(void *arg);
int stock = 0;
//### Il sigset_t viene utilizzato per rappresentare un signal set
sigset_t mask;
int main(){
printf("\tIPv4 UDP Server app\n");
int port1 = PORT, port2 = PORT2, port3 = PORT3;
//INITIALITE THREAD AND VARIABLES FOR THREAD
int thres; //response of the thread
pthread_t a_thread, a_thread2, a_thread3;
void *thread_result;
//CREATE THREAD 1
thres = pthread_create(&a_thread, NULL, thread_function, &port1);
if (thres != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
//CREATE THREAD 2
thres = pthread_create(&a_thread2, NULL, thread_function, &port2);
if (thres != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
//CREATE THREAD 3
thres = pthread_create(&a_thread3, NULL, thread_function, &port3);
if (thres != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
thres = pthread_join(a_thread, &thread_result);
if (thres != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
thres = pthread_join(a_thread2, &thread_result);
if (thres != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
thres = pthread_join(a_thread3, &thread_result);
if (thres != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
return 0;
}
void *thread_function(void *arg) {
pid_t pid;
int parentpid = getpid();
printf("Il pid della thread vale %d\n",parentpid);
int port = *((int *) arg);
//### Nella variabile signo salverò il numero di segnale che mi restituisce la wait
int err, signo;
printf("Porta %d\n",port);
printf("ENTER TO THREAD\n");
int res = 0; //valore di ritorno delle APIs
/*
socket: servirà per la comunicazione col server
*/
int sockfd = 0;
/*
open socket di tipo datagram
*/
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror("socket error: ");
return FAILURE;
}
/*
indirizzo IPv4 del server, senza hostname resolution
*/
struct sockaddr_in server;
socklen_t len = sizeof(server);
memset(&server, 0, sizeof(server)); //azzero la struttura dati
server.sin_family = AF_INET; //specifico l'Address Family IPv4
/*
Specifico l'indirizzo del server: qualsiasi interfaccia
*/
server.sin_addr.s_addr = htonl(INADDR_ANY);
/*
Specifico la well-known port
*/
server.sin_port = htons(port);
//setto l'indirizzo well-known del socket
res = bind(sockfd, (struct sockaddr *)&server, len);
if (res == -1)
{
perror("Bind error: ");
close(sockfd);
exit(1);
}//fi
ssize_t n = 0;
char buffer[BUFSIZE];
struct sockaddr_in client;
char address[INET_ADDRSTRLEN] = "";
int quit = 0;
if ((pid = fork()) < 0) {
printf("Fork error<n");
} else if (pid == 0) { /* child */
printf("child dal pid %d\n",getpid());
while(1){
//### La sigwait non fa che attendere l'arrivo di un segnale
err = sigwait(&mask, &signo);
//### Una volta terminata la sigwait, ossia quando ha ricevuto un segnale
//(se no è successo un errore), possiamo switchare il segnale arrivato, che
//è salvato in signo
switch (signo) {
//### Ora gestisco, se gli ho mandato un SIGUSR1, ossia un flag che, arrivato
//a destinazione indica di eseguire quel segnale che il processo che lo riceve
//lo implementa come vuole. Si sa che si deve eseguire quel segnale, di libera
//interpretazione
case SIGUSR1:
printf("Sigusr1\n");
printf("Stock vale %d\n",stock);
break;
//### In caso gli mandassi SIGINT, valuta sempre se ci sono errori e poi esci
case SIGINT:
printf("Sigint\n");
//### Di default comunque, controlla se ci sono errori e poi esci
default:
stock++;
printf("Sto producendo %d dalla porta %d\n",stock, port);
sleep(2);
}
}
} else {
printf("parent dal pid %d\n",getpid()); /* parent */
while (!quit)
{
printf("Main thread entrato nel ciclo\n");
n = recvfrom(sockfd, buffer, BUFSIZE-1, 0, (struct sockaddr *)&client, &len);
printf("Mando segnali");
//### Inizializza il segnale settato set a escludere tutti i segnali definiti
sigemptyset(&mask);
//### Aggiunge il segnale signum al segnale settato set. Con sigaddset modifichiamo
//set, ma non blocchiamo o sblocchiamo alcun segnale!
//Mettiamoli quindi SIGURS1
sigaddset(&mask, SIGUSR1);
//### E anche SIGINT
sigaddset(&mask, SIGINT);
//### pthread_sigmask esamina e cambia segnali bloccati. Con SIG_BLOCK il set
//risultante è l'unione del set corrente e il set di segnale indicato da set
if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0)
{
fprintf(stderr, "thread mask failed\n");
exit(EXIT_FAILURE);
}
kill(parentpid+1,SIGUSR1);
printf("Segnale inviato al pid %d\n",(parentpid+1));
if (n == -1)
{
perror("recvfrom() error: ");
continue;
// close(sockfd);
// return FAILURE;
}
}//wend
printf("Stockmann %d\n",stock);
//qui non ci arrivo mai...
close(sockfd);
}
return NULL;
}
/** #} */
this is Header.h (but it's not necessary to see it)
/**
#addtogroup Group11
#{
*/
/**
#file Header.h
#author Catiuscia Melle
#brief Header comune al client e server dell'esempio
L'header definisce costanti comuni al client e server.
*/
#ifndef __HEADER_H__
#define __HEADER_H__
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> //resolver related functions
#include <sys/types.h> //necessario su Mac
#include <stdlib.h>
#include <ctype.h> //toupper()
#include <stdbool.h>
//THIS FOR RAND FUNCTION
#include <stdlib.h>
#include <time.h>
//THIS FOR THREADS
#include <pthread.h>
#define PORTNUMBER 49152 /**< UDP listening port, decimal */
#define PORT 49152 /**< UDP listening port, decimal */
#define PORT2 49153 /**< UDP listening port, decimal */
#define PORT3 49154 /**< UDP listening port, decimal */
#define SERVICEPORT "49152" /**< UDP listening port, name */
#define PORT_STRLEN 6 /**< lunghezza di una stringa rappresentativa di un indirizzo di trasporto */
#define BACKLOG 10 /**< dimensione della coda di connessioni */
#define SIZE 15 /**< dimensione della coda di connessioni concorrenti gestite dal server */
#define BUFSIZE 512 /**< dimensione del buffer di messaggio */
#define FAILURE 3 /**< definizione del valore di errore di ritorno del processo in caso di errori delle Sockets API */
#define INVALID 5 /**< se i due processi non sono avviati col giusto numero di parametri */
#define ERROR 1 /**< valore di errore */
#define INSUCCESS -1 /**< valore di ritorno non valido */
#define SEMAPHORE_NAME "gbrunetta317"
#endif /* __HEADER_H__ */
/** #} */
The problem is that when I send something with the client, the value inside case: Sigurs1 doesn't appear:
Actually, it doesn't returns nothing from the SIGUSR1 defined in child, but you can see that it stop that process (in fact you can see that it doesn't work again with port 49512, because pidparent+1 is associated with that port). I hope I was clear. How I can solve it?
I discovered that it fail with shmat.. I don't know why, but I rewrite the code according with this example: How to trigger SIGUSR1 and SIGUSR2?
And now it works fine.
I'm sorry but I'm a beginner in C Socket Programming. I have a Server and more client. When a Client send a message to the Server, the Server should forward this message to all Clients.
I designed Server and Client using the Select(), now I don't know how I can do to send in broadcast the message to all Clients, in broadcast way.
This is my Server side called SelectServer.c:
#include "Header.h"
#include "Utility.h"
/**
variabili globale client sockets array
rappresenta l'array di socket descriptor connessi ai client TCP
*/
int conn_set[SIZE];
/**
#brief aggiunge la nuova connessione all'array di client connessi
#param sockfd, il socket della nuova connessione
#param conn_set, ptr all'array delle connessioni
#param dim, dimensione dell'array
#return true se la nuova connessione è stata registrata, false se la coda è piena
*/
bool add_client(int sockfd, int *conn_set, int dim){
int i = 0;
bool is_registered = false;
for (i = 0; i < dim; i++)
{
if (conn_set[i] == -1)
{
conn_set[i] = sockfd;
is_registered = true;
break;
}
}
return is_registered;
}
/**
#brief Funzione che gestisce l'arrivo di nuove connessioni sul listening socket TCP.
#param sockT - listening TCP socket del server
#return intero:
-# pari a -1 se la connessione è stata rifiutata.
-# pari al socket descriptor della nuova connessione (se correttamente inserita in elenco)
*/
int handleNewConn(int sockT){
struct sockaddr_storage client;
socklen_t sslen = sizeof(client);
int conn = accept(sockT, (struct sockaddr *)&client, &sslen);
if (conn == -1)
{
perror("accept() error: ");
return -1;
}
printf("TCP listening socket accepted new connection from ");
printAddressInfo((struct sockaddr *)&client, sslen);
printf("\n");
bool is_added = add_client(conn, conn_set, SIZE);
if (is_added == false)
{
printf("max client queue\n");
close(conn);
return -1;
}
return conn;
}
/**
#brief Inizializza l'array dei socket connessi
#param conn_set, ptr all'arrayd di interi
#param dim, dimensione dell'array
*/
void setConnSet(int *conn_set, int dim){
int i = 0;
for (i = 0; i < dim; i++)
{
conn_set[i] = -1;
}
}
/**
#brief Funzione di utilità
#param name, nome dell'eseguibile
*/
void usage(char *name){
printf("Usage: %s <domain>\n", name);
printf("\tdomain: 0 (UNSPEC), 4 (INET), 6 (INET6)\n");
}
int main(int argc, char *argv[]){
//*********************
char buf2[50000];
int posxy[2];
int pos[100][100];
int x, y;
//*********************
if (argc != 2)
{
usage(argv[0]);
return INVALID;
}
int family = getFamily(argv[1]);
int sockT = 0; //TCP listening socket
int sockU = 0; //UDP listening socket
sockT = passiveOpen(family, NULL, SERVICEPORT, SOCK_STREAM);
sockU = passiveOpen(family, NULL, SERVICEPORT, SOCK_DGRAM);
if ((sockT == -1) || (sockU == -1))
{
printf("Errore nell'avviare i socket\n");
return ERROR;
}
/* su Mac OS X:
$ netstat -a | grep 49152
tcp4 0 0 *.49152 *.* LISTEN
udp4 0 0 *.49152 *.*
*/
printf("TCP Server and UDP server running on port %s\n", SERVICEPORT);
//inizializzo l'array
setConnSet(conn_set, SIZE);
//handle delle nuove connessioni (socket connessi)
int conn = 0;
//protocol-independent management of connections
struct sockaddr_storage client;
socklen_t sslen = sizeof(client);
/*
definiamo il set dei file descriptor da monitorare:
il server sarà in attesa di:
- connessioni
- messaggi,
- e tutte condizioni monitorabili in lettura
*/
//dichiaro 2 read file descriptor set
fd_set rset, allset;
FD_ZERO(&rset); //azzero set descrittori
FD_ZERO(&allset); //azzero set descrittori copia
FD_SET(sockT, &rset); //aggiungo socket TCP
FD_SET(sockU, &rset); //aggiungo socket UDP
//prendo il max tra i 2, più 1
int max = (sockT > sockU)?sockT+1: sockU+1;
//duplico il set - allset contiene sempre i 2 socket TCP ed UDP
allset = rset;
//***************
ssize_t n = 0;
//***************
int result = 0;
int quit = 0;
while (!quit)
{
/*
rfds è un value-result argument, modificato da select():
dobbiamo re-inizializzarlo
*/
FD_ZERO(&rset);
rset = allset;
//wait for select forever
result = select(max, &rset, NULL, NULL, NULL);
if (result == -1)
{
perror("select() error: ");
continue;
}
else if (result == 0)
{
//timeout su select (non specificato, non dovrei mai arrivarci)
continue;
}
else
{
//result è pari al numero di descriptor pronti in lettura...
printf("%d socket ready for reading\n", result);
//verifichiamo chi ha dati da leggere...
if ( FD_ISSET(sockU, &rset))
{
//*******
n = recv(sockU, posxy, sizeof(posxy), 0);
printf ("Prova %d",posxy[0]);
//******
printf("Received a datagram...\n");
handleUDPMessage(sockU);
//printf("decrement result...\n");
result--;
}
if ( FD_ISSET(sockT, &rset))
{
//new TCP Connection
conn = handleNewConn(sockT);
if (conn != -1)
{
//printf("add new conn to allset\n");
FD_SET(conn, &allset);
if ( (conn + 1) > max)
max = conn+1;
}
result--;
}
if (result == 0)
{
//skip the rest
continue;
}
//a questo punto devo controllare tutte le connessioni attive dei client
int j = 0; //indice dell'array di connessioni TCP client
while (result > 0)
{
if ( (conn_set[j] != -1) && FD_ISSET(conn_set[j], &rset))
{
printf("Client Connection number %d ready for reading ...\n", j);
int status = handleTCPClient(conn_set[j]);
//decremento result
result--;
if (status == 0)
{
//client ha chiuso la connessione
close(conn_set[j]);
//rimuovo il socket dal set da monitorare
FD_CLR(conn_set[j], &allset);
//libero la sua posizione nell'array
conn_set[j] = -1;
}//fi status
}//fi FD_ISSET
//passo al prossimo elemento dell'array di connessioni TCP
j++;
}//wend client connection
}//fi result
}//wend
//never here
close(sockT);
close(sockU);
return 0;
}
This is my Client side called SelectClient.c:
#include "Header.h"
#include "Utility.h"
/**
#brief Utility function
#param name, nome dell'eseguibile
*/
void usage(char *name){
printf("Usage: %s <servername> <protocol> <domain>\n", name);
printf("\tprotocol= TCP aut UDP;\n");
printf("\tdomain= 0 (UNSPEC), 4(INET), 6(INET6)\n");
}
/**
#brief Esegue l'I/O multiplexing su stdin e socket connesso (UDP/TCP)
#param sock, il socket da monitorare
#param type, tipo di socket
#return nulla
*/
void multiplex(int sock, int type){
//*******************************
int pos[100][100];
int posxy[2];
int x,y,xx,yy;
char movimento;
for(x=0;x<100;x++)
for(y=0;y<100; y++)
pos[x][y]=0;
time_t tt;
/* Intializes random number generator */
srand((unsigned) time(&tt));
xx = rand() % 50;
yy = rand() % 50;
printf("xx vale %d, yy vale %d",xx,yy);
pos[xx][yy] = 1;
printf("MAPPA INIZIALE:\n");
for(x=0;x<100;x++)
for(y=0;y<100; y++)
printf("%d",pos[x][y]);
//*******************************
printf("Insert messages for server\n");
fd_set rset;
FD_ZERO(&rset);
FD_SET(STDIN_FILENO, &rset);
FD_SET(sock, &rset);
int max = (sock > STDIN_FILENO)? sock: STDIN_FILENO;
struct timeval timer;
timer.tv_sec = 5;
timer.tv_usec = 0;
int result = 0;
bool quit = false;
//predispongo la comunicazione ed il file descriptor set da monitorare
ssize_t n = 0;
char msg[BUFSIZE] = "";
//***********************************
int msgpos[100][100];
char msgposc[50000] = "";
//***********************************
while (!quit)
{
result = select(max+1, &rset, NULL, NULL, &timer);
if (result == -1)
{
perror("select() error: ");
break;
}
if (result == 0)
{
//printf("select timeout\n");
}
if (result > 0)
{
if (FD_ISSET(STDIN_FILENO, &rset))
{
if (fgets(msg, BUFSIZE-1, stdin) != NULL)
{
//**************************
printf("scrivi\n");
//scanf("%c",&movimento);
movimento = getchar();
printf("movimento %c\n",movimento);
if(movimento == 'j'){
printf("destra\n");
pos[xx][yy] = 0;
yy++;
printf("xx vale %d, yy vale %d",xx,yy);
pos[xx][yy] = 1;
}
else if(movimento == 'h'){
printf("sinistra\n");
pos[xx][yy] = 0;
yy--;
printf("xx vale %d, yy vale %d",xx,yy);
pos[xx][yy] = 1;
}
else if(movimento == 'u'){
printf("sopra\n");
pos[xx][yy] = 0;
xx++;
printf("xx vale %d, yy vale %d",xx,yy);
pos[xx][yy] = 1;
}
else if(movimento == 'n'){
printf("sotto\n");
pos[xx][yy] = 0;
xx--;
printf("xx vale %d, yy vale %d",xx,yy);
pos[xx][yy] = 1;
}
else
printf("Non hai inserito un comando valido\n");
for(x=0;x<100;x++)
for(y=0;y<100; y++)
printf("%d",pos[x][y]);
//snprintf(msgposc, 50000, "%d", pos);
//snprintf(posx, 10, "%d", xx);
//snprintf(posy, 10, "%d", yy);
//printf("PROVAaa: %s \n", posx);
posxy[0] = xx;
posxy[1] = yy;
//**************************
/*
fgets ritorna una stringa che termina con la sequenza
'\n\0'
il null-terminated non è contato da strlen,
ma '\n' si.
Per tagliare '\n' trasmetto (strlen(msg) - 1).
#note: ATTENZIONE
Ogni volta che premo "Return" (dò invio), fgets() ritorna una stringa è vuota.
La stringa vuota viene letta da send che trasmette 0 bytes dal socket "sock".
Dobbiamo distinguere 2 casi:
- se sock è di tipo SOCK_STREAM, l'operazione non genera dati da trasmettere;
- se sock è di tipo SOCK_DGRAM, l'operazione genera un datagram UDP vuoto, che viene inviato al server UDP
Per eliminare questo scenario, possiamo effettuare send solo su stringhe non vuote.
*/
//if ( (strlen(msg) - 1) != 0){
n = send(sock, posxy, sizeof(posxy), 0);
//n = send(sock, msg, strlen(msg) - 1, 0);
printf("sent %d bytes\n", (int)n);
//}//fi
}
else
break; //chiusura
}
if (FD_ISSET(sock, &rset))
{
//n = recv(sock, msg, BUFSIZE-1, 0);
//******************************
n = recv(sock, posxy, sizeof(posxy), 0);
//*******************************
if (n == -1)
{
perror("recv() error: ");
close(sock);
return; // ERROR;
}
else if (n > 0)
{
//**************************
msg[n] = 0;
msgpos[n][n]=0;
printf("mappa:\n");
printf("server reply: '%d'\n", posxy[0]);
//**************************
// msg[n] = 0;
// printf("\tResponse %d bytes message '%s'\n", (int)n, msg);
}
else
{
//n==0 over TCP: closed connection
//if (type == SOCK_STREAM)
// break;
/*
In realtà non abbiamo necessità di usare il parametro type in input alla funzione,
perché il tipo del socket può essere ottenuto leggendo le opzioni del socket:
*/
int sockType = 0;
socklen_t optlen = sizeof(sockType);
if ( getsockopt(sock, SOL_SOCKET, SO_TYPE, &sockType, &optlen) == 0){
if (sockType == SOCK_STREAM) {
printf("This is a TCP socket that received a FIN segment\n");
break;
} else {
printf("This is an UDP socket that received an empty datagram\n");
}
}//fi getsockopt
}
}//fi sock
}//fi result
FD_ZERO(&rset);
FD_SET(STDIN_FILENO, &rset);
FD_SET(sock, &rset);
max = (sock > STDIN_FILENO)? sock: STDIN_FILENO;
timer.tv_sec = 5;
timer.tv_usec = 0;
}//wend
printf("Multiplex ended\n");
}
int main(int argc, char *argv[]){
if (argc != 4)
{
usage(argv[0]);
return INVALID;
}
int family = getFamily(argv[3]);
int type = 0;
if ((strcmp(argv[2], "TCP") == 0) || (strcmp(argv[2],"tcp") == 0))
{
type = SOCK_STREAM;
}
else if (strcmp(argv[2], "UDP") == 0 || (strcmp(argv[2],"udp") == 0))
{
type = SOCK_DGRAM;
}
else
{
printf("Invalid service type\n");
return FAILURE;
}
int sockfd = open_socket(family, argv[1], SERVICEPORT, type);
if (sockfd == INSUCCESS)
{
printf("Errore nell'aprire il socket e stabilire la connessione al server (TCP only)\n");
return ERROR;
}
//ho un socket CONNESSO verso la destinazione specificata:
struct sockaddr destination;
socklen_t len = sizeof(destination);
int res = getpeername(sockfd, &destination, &len);
if (res != 0) {
close(sockfd);
return FAILURE;
}
//visualizzo il remote address
printf("Connected to remote address: ");
printAddressInfo(&destination, len);
printf("\n");
multiplex(sockfd, type);
printf("Closing the socket...\n");
close(sockfd);
return 0;
}
This is Utility.c (where are implemented some functions):
/**
#addtogroup Group11
#{
*/
/**
#file Utility.c
#author Catiuscia Melle
#brief Implementazione di funzioni di utilità.
*/
#include "Utility.h"
void printAddressInfo(struct sockaddr * addr, socklen_t len){
//no reverse lookup in getnameinfo
int niflags = NI_NUMERICSERV | NI_NUMERICHOST;
char IP[INET6_ADDRSTRLEN] = "";
char port[PORT_STRLEN] = "";
//visualizzo l'indirizzo locale del socket
int rv = getnameinfo(addr, len, IP, INET6_ADDRSTRLEN, port, PORT_STRLEN, niflags);
if (rv == 0)
{
printf("'%s:%s'", IP, port);
}
else
{
printf("getnameinfo() error: %s\n", gai_strerror(rv));
}
}
int open_socket(int family, char *hostname, char *servicename, int type){
int sockfd = 0; //valore di ritorno della funzione
int result = 0;
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints)); //azzero hints
hints.ai_family = family; //richiedo la risoluzione per IPv6
hints.ai_socktype = type; //service type
/*
Se richiedo AF_INET6 come family,
allora specificando il flag AI_V4MAPPED,
getaddrinfo() deve ritornare l'IPv4-mapped IPv6 address
se non trova indirizzi IPv6.
Diversamente, l'opzione non è presa in considerazione
*/
hints.ai_flags = AI_V4MAPPED;
result = getaddrinfo(hostname, servicename, &hints, &res);
if (result != 0)
{
printf("getaddrinfo: %s\n", gai_strerror(result));
return INSUCCESS;
}
printf("Resolution for: %s:%s Done!\n", hostname, servicename);
for (p = res; p != NULL; p = p->ai_next)
{
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd == -1)
{
perror("socket() error: ");
continue;
}
//TCP o UDP, il socket viene comunque connesso al destination address
result = connect(sockfd, p->ai_addr, p->ai_addrlen);
if (result == -1)
{
perror("connect() error: ");
close(sockfd);
continue;
}
break;
}//for
if (p == NULL) //if (!p)
{
printf("Connessione al server %s:%s fallita\n", hostname, servicename);
return INSUCCESS;
}
//dealloco risorse resolver
freeaddrinfo(res);
return sockfd;
}
int getFamily(char *param){
int family = 0;
if (strncmp(param, "4", 1) == 0){
family = AF_INET;
} else if (strncmp(param, "6", 1) == 0){
family = AF_INET6;
} else {
family = AF_UNSPEC;
}
return family;
}
/*************************************************************************/
int passiveOpen(int family, char *nodename, char *servicename, int socktype) {
int sock = INSUCCESS;
int result = 0;
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = socktype;
hints.ai_flags |= AI_V4MAPPED;
hints.ai_flags |= AI_NUMERICSERV;
result = getaddrinfo(nodename, servicename, &hints, &res);
if (result != 0)
{
printf("getaddrinfo: %s\n", gai_strerror(result));
return INSUCCESS;
}
int reuse = 1; //opzione SO_REUSEADDR
for (p = res; p != NULL ; p = p->ai_next)
{
sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sock == -1)
{
perror("socket() error: ");
continue;
}
/*
Per un socket nel dominio AF_INET6, se l'opzione IPV6_V6ONLY è abilitata
viene meno la compatilibità con IPv4: il socket accetterà comunicazioni solo se
provenienti da nodi IPv6 (ritornando l'errore 'connection refused' al client che
cerca di connettersi).
Altrimenti, al socket sono consegnati anche datagram IPv4.
Per abilitare l'opzione:
*/
int v6flag = 1;
if (v6flag == 1)
{
//turn-on V6ONLY option
if (p->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &v6flag, sizeof(v6flag)) < 0)
{
close(sock);
continue;
}
}
if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0 )
{
perror("setsockopt() error: ");
continue;
}
result = bind(sock, p->ai_addr, p->ai_addrlen);
if (result == -1)
{
perror("bind() error: ");
close(sock);
continue;
}
break;
}//for
if (p == NULL)
{
printf("Non siamo riusciti ad avviare il server %s:%s per il servizio %d\n", nodename, servicename, socktype);
sock = INSUCCESS;
}
//dealloco le risorse
freeaddrinfo(res);
if (socktype == SOCK_STREAM)
{
//TCP passive open
result = listen(sock, BACKLOG);
if (result == -1)
{
perror("listen() error: ");
close(sock);
return INSUCCESS;
}
}
return sock;
}
void handleUDPMessage(int sockU){
//protocol-independent addresses management
struct sockaddr_storage client;
socklen_t sslen = sizeof(client);
char msg[BUFSIZE] = "";
ssize_t n = 0;
int posxy[2];
//n = recvfrom(sockU, msg, BUFSIZE-1 , 0, (struct sockaddr *)&client, &sslen);
//*******************
n = recvfrom(sockU, posxy, sizeof(posxy), 0, (struct sockaddr *)&client, &sslen);
//*****************
if (n < 0)
{
perror("error on recvfrom(): ");
return;
}
printAddressInfo((struct sockaddr *)&client, sslen);
msg[n] = '\0';
//printf("Received UDP %d bytes message '%s'\n", (int)n, msg);
printf("Received UDP %d bytes message '%d'\n", (int)n, posxy[0]);
char c = toupper(msg[n-1]);
msg[n-1] = toupper(msg[0]);
msg[0] = c;
printf("Reply to msg\n");
//n = sendto(sockU, msg, strlen(msg), 0, (struct sockaddr *)&client, sslen);
n = sendto(sockU, posxy, sizeof(posxy), 0, (struct sockaddr *)&client, sslen);
if (n == -1)
{
perror("sendto() error: ");
}
return;
}
int handleTCPClient(int sock){
char msg[BUFSIZE] = "";
ssize_t n = 0;
int posxy[2];
//get the message from client
//n = recv(sock, msg, BUFSIZE-1, 0);
n = recv(sock, posxy, sizeof(posxy), 0);
if (n > 0)
{
//protocol-independent management of connections
struct sockaddr_storage client;
socklen_t sslen = sizeof(client);
char ipstr[INET6_ADDRSTRLEN] = "";
char portstr[INET6_ADDRSTRLEN] = "";
getpeername(sock, (struct sockaddr *)&client, &sslen);
int niflags = NI_NUMERICSERV | NI_NUMERICHOST;
int res = getnameinfo( (struct sockaddr *)&client, sslen, \
ipstr, INET6_ADDRSTRLEN, portstr, INET6_ADDRSTRLEN, niflags);
if (res == 0)
{
printf("Received TCP message from client da %s:%s\n", ipstr, portstr);
}
msg[n] = '\0';
//printf("\tmessage '%s'\n\t%d bytes\n", msg, (int)n);
printf("\tmessage '%d'\n\t%d bytes\n", posxy[0], (int)n);
msg[0] = toupper(msg[0]);
msg[n/2] = toupper(msg[n/2]);
msg[n-1] = toupper(msg[n-1]);
//n = send(sock, msg, strlen(msg), 0);
n = send(sock, posxy, sizeof(posxy), 0);
if (n == -1)
{
perror("send() error: ");
}
return 1;
}
if (n == 0)
{
printf("il client ha chiuso la connessione\n");
return 0;
}
if (n < 0)
{
perror("error on recv(): ");
return -1;
}
return -1;
}
and these are my libraries called Utility.h and Header.h:
/**
#addtogroup Group11
#{
*/
/**
#file Utility.h
#author Catiuscia Melle
#brief Interfaccia del modulo di funzioni di utilità.
*/
#ifndef __UTILITY_H__
#define __UTILITY_H__
#include "Header.h"
/**
#brief Utility function per la visualizzazione dell'indirizzo associato ad un socket,
protocol-independent
#param addr, ptr alla struct sockaddr da leggere
#param len, dimensione della struttura puntata da addr
#return nulla
*/
void printAddressInfo(struct sockaddr * addr, socklen_t len);
/**
#brief Utility per l'apertura di un connection socket TCP IPv6
#param family - ipv4 o ipv6 protocol family domain...
#param hostname - stringa contenente l'hostname per cui è invocato il resolver
#param servicename - stringa contenente il service name per cui è invocato il resolver
#param type - intero pari al tipo di socket SOCK_STREAM o SOCK_DGRAM
#return intero, pari al socket descriptor creato (-1 in caso di errore).
Attenzione, restituisce sempre un socket connesso (UDP e TCP)
*/
int open_socket(int family, char *hostname, char *servicename, int type);
/**
#brief Legge il valore di param e ritorna l'Address Family corrispondente
#param param, contiene il valore di argv[] corrispondente al dominio inserito.
#return il valore di Address Family corrispondente al dominio specificato
*/
int getFamily(char *param);
/**
#brief Funzione per la hostname resolution che ritorna il socket descriptor per un server.
#param nodename - hostname da risolvere
#param servicename - indirizzo del servizio
#param socktype - tipo di sock (SOCK_STREAM, SOCK_DGRAM)
#return intero pari al socket descriptor allocato o -1 in caso di errore
*/
int passiveOpen(int family, char *nodename, char *servicename, int socktype);
/**
#brief Funzione che gestisce l'arrivo di messaggi sul socket UDP in ascolto
#param sockU - socket UDP in ascolto
#return nulla
Effettua ricezione ed invio al client UDP. Usa <em>getnameinfo()</em> per recuperare
l'indirizzo del client UDP.
*/
void handleUDPMessage(int sockU);
/**
#brief Funzione che gestisce l'arrivo di nuovi messaggi su connessioni TCP.
#param sock - socket TCP della connessione client pronta.
#return intero:
-# pari a -1 in caso di errore;
-# pari a 0 se la connessione è stata chiusa;
-# pari a 1 se il messaggio è stato elaborato con successo.
*/
int handleTCPClient(int sock);
#endif
/** #} */
/**
#addtogroup Group11
#{
*/
/**
#file Header.h
#author Catiuscia Melle
#brief Header comune al client e server dell'esempio
L'header definisce costanti comuni al client e server.
*/
#ifndef __HEADER_H__
#define __HEADER_H__
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> //resolver related functions
#include <sys/types.h> //necessario su Mac
#include <stdlib.h>
#include <ctype.h> //toupper()
#include <stdbool.h>
#define PORTNUMBER 49152 /**< TCP listening port, decimal */
#define SERVICEPORT "49152" /**< TCP listening port, name */
#define PORT_STRLEN 6 /**< lunghezza di una stringa rappresentativa di un indirizzo di trasporto */
#define BACKLOG 10 /**< dimensione della coda di connessioni */
#define SIZE 15 /**< dimensione della coda di connessioni concorrenti gestite dal server */
#define BUFSIZE 512 /**< dimensione del buffer di messaggio */
#define FAILURE 3 /**< definizione del valore di errore di ritorno del processo in caso di errori delle Sockets API */
#define INVALID 5 /**< se i due processi non sono avviati col giusto numero di parametri */
#define ERROR 1 /**< valore di errore */
#define INSUCCESS -1 /**< valore di ritorno non valido */
#endif /* __HEADER_H__ */
/** #} */
In particular, look that the message that I send from Client to Server is called "posxy". The Server receive "posxy", print it and send it again to the client who sent. But if I open more Client, the Server send "posxy" only at the Client who sent it, I want to send "posxy" from Server al all Clients connected.
I hope that somebody can help me.
Here's a simple code tcp client/server where the server sends "Hello from Server" after connection established. My problems are:
1) client writes the string after i close the server.exe window and don't know why;
2) client prints strange characters and not "Hello from server". I miss something in the output format.
SERVER
#if defined WIN32
#include <winsock2.h>
#else
#define closesocket close
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#define PROTOPORT 5193 //default protocol port number
#define QLEN 6 // size of request queue
#define BUFFERSIZE 10
void ErrorHandler (char *errorMessage) {
printf (errorMessage);
}
void ClearWinSock() {
#if defined WIN32
WSACleanup ();
#endif
}
int main(void) {
#if defined WIN32
WSADATA wsaData;
int iResult = WSAStartup (MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
ErrorHandler ("Error at WSAStartup()\n");
return 0;
}
#endif
// CREAZIONE DELLA SOCKET
int MySocket;
MySocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (MySocket < 0) {
ErrorHandler ("socket creation failed.\n");
ClearWinSock();
return 0;
}
// ASSEGNAZIONE DI UN INDIRIZZO ALLA SOCKET
struct sockaddr_in sad;
memset (&sad, 0, sizeof (sad)); //ensures that extra bytes contain 0
sad.sin_family = AF_INET;
sad.sin_addr.s_addr = inet_addr ("127.0.0.1"); //ip del server con conversione
//da notazione dotted-decimal in un numero a 32 bit
//espresso nella rappresentazione della rete
sad.sin_port = htons (5193); //host to network short
//Assegnazione porta e ip alla socket e verifica presenza di eventuali errori
if (bind (MySocket, (struct sockaddr*) & sad, sizeof (sad)) < 0) {
ErrorHandler ("bind() failed.\n");
closesocket (MySocket);
ClearWinSock ();
return 0;
}
// SETTAGGIO DELLA SOCKET ALL'ASCOLTO
if (listen (MySocket, QLEN) < 0) {
ErrorHandler ("listen() failed.\n");
closesocket (MySocket);
ClearWinSock ();
return 0;
}
// ACCETTARE UNA NUOVA CONNESSIONE - e creazione di una nuova socket per comunicare con il client
struct sockaddr_in cad; //structure for the client address
int clientSocket; // socket descriptor for the client
int clientLen; //the size of the client address
printf ("Waiting for a client to connect...");
while (1) {
clientLen = sizeof (cad); //set the size of the client address
if ((clientSocket = accept (MySocket, (struct sockaddr*) &cad, &clientLen)) < 0) {
ErrorHandler ("accept() failed.\n");
// CHIUSURA DELLA CONNESSIONE
closesocket (MySocket);
ClearWinSock ();
return 0;
}
printf ("Handling client %s\n", inet_ntoa (cad.sin_addr));
}
char* inputString = "Hello from server"; // Stringa da inviare
int stringLen = strlen (inputString); // Determina la lunghezza
// INVIARE DATI AL CLIENT
if (send (clientSocket, inputString, stringLen, 0) != stringLen) {
ErrorHandler ("send () sent a different number of bytes than expected");
closesocket (clientSocket);
ClearWinSock();
system("pause");
return 0;
}
closesocket(MySocket);
ClearWinSock ();
return 0;
}
CLIENT
#if defined WIN32
#include <winsock2.h>
#else
#define closesocket close
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#define BUFFERSIZE 20 // Dimensione buffer che riceve dati dal server
#define PROTOPORT 5193 // Numero di porta di default
void ErrorHandler (char *errorMessage) {
printf (errorMessage);
}
void ClearWinSock () {
#if defined WIN32
WSACleanup();
#endif
}
int main (void) {
#if defined WIN32
WSADATA wsaData;
int iResult = WSAStartup (MAKEWORD (2,2), &wsaData);
if (iResult !=0) {
printf ("error at WSASturtup\n");
return 0;
}
#endif
// CREAZIONE DELLA SOCKET
int Csocket;
Csocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Csocket < 0) {
ErrorHandler ("socket creation failed.\n");
closesocket (Csocket);
ClearWinSock ();
return 0;
}
// COSTRUZIONE DELL'INDIRIZZO DEL SERVER
struct sockaddr_in sad;
memset (&sad, 0, sizeof (sad));
sad.sin_family = AF_INET;
sad.sin_addr.s_addr = inet_addr ("127.0.0.1"); //ip del server con conversione
//da notazione dotted-decimal in un numero a 32 bit
//espresso nella rappresentazione della rete
sad.sin_port = htons (5193); // Server port
//CONNESSIONE AL SERVER
if (connect (Csocket, (struct sockaddr*) &sad, sizeof (sad)) < 0) {
ErrorHandler ("Failed to connect.\n");
closesocket (Csocket);
ClearWinSock();
return 0;
}
char buf[BUFFERSIZE];
recv (Csocket, buf, BUFFERSIZE - 1, 0);
printf("Server scrive: %s\n",buf);
// CHIUSURA DELLA CONNESSIONE
closesocket (Csocket);
ClearWinSock();
printf ("\n");
system ("pause");
return 0;
}
In the end: If I would to send more strings, have I to use one send() and one recv() for each of them?
The biggest issue is that you do not break out of the server loop. This is why you don't see anything on the client until you quit the server. The socket gets closed when the program terminates, so the recv call in client no longer blocks. Also, in client you don't check return val from recv and print junk.
Server fix:
while (1) {
clientLen = sizeof (cad); //set the size of the client address
if ((clientSocket = accept (MySocket, (struct sockaddr*) &cad, &clientLen)) < 0) {
....
}
printf ("Handling client %s\n", inet_ntoa (cad.sin_addr));
break; // <- terminate loop,
// or just get rid of the loop altogether as there
// is no real need for it I can see
}
Client fix:
int read = recv (Csocket, buf, BUFFERSIZE - 1, 0);
if (read <= 0) {
// Not successful
}
else {
buf[read] = 0; // Add eos
printf("%s", buf);
}
I'm trying to do an echo server using SSL and verifying certificates of both sides. The socket connection seems to work perfectly but the SSL_Accept on the server returns error. The error is:
CONEXION: 127.0.0.1:55387 3073529532:error:140890B2:SSL routines: \
SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:3283:
This is the client code:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
//carga los certificados como en el servidor
void CargaCertificados(SSL_CTX* ctx, char* CertFile, char* KeyFile){
/* setea el certificado local */
if (SSL_CTX_use_certificate_file(ctx, CertFile , SSL_FILETYPE_PEM) <= 0){
ERR_print_errors_fp(stderr);
abort();
}
/* setea la clave privada */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 ){
ERR_print_errors_fp(stderr);
abort();
}
/* verifica la clave prievada */
if ( !SSL_CTX_check_private_key(ctx) ){
fprintf(stderr, "la clave privada no coincide con el certificado publico\n");
abort();
}
}
//conecta el socket como en las practicas anteriores
int ConectaSocket(const char *hostname, int port){
int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL ){
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ){
close(sd);
perror(hostname);
abort();
}
return sd;
}
//inicializa el contexto a traves de una instancia
SSL_CTX* InitCTX(void){
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* carga los cifrados etc */
SSL_load_error_strings(); /* carga los mensajes de error */
ctx = SSL_CTX_new(SSLv23_client_method()); /* crea un nuevo contexto con la instancia del cliente*/
if ( ctx == NULL ){
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
//muestra los certificados
void MuestraCertificados(SSL* ssl){
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* coge el certificado del servidor */
if ( cert != NULL ){
printf("CERTIFICADOS EN EL CLIENTE:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("SUJETO: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("EMISOR: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("NO EXISTEN CERTIFICADOS.\n");
}
int main(int argc, char*argv[]){
SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
int bytes;
char msg[100];
if(argc != 4)
return -1;
SSL_library_init();
ctx = InitCTX();
CargaCertificados(ctx, argv[2], argv[3]);
server = ConectaSocket("localhost", atoi(argv[1]));
ssl = SSL_new(ctx); /* crea el estado de conexion SSL */
SSL_set_fd(ssl, server); /* setea el socket a conexion ssl */
if ( SSL_connect(ssl) < 0 ) /* conecta con el servidor */
ERR_print_errors_fp(stderr);
else{
printf("CONECTADO CON ENCRIPTACION %s\n\n", SSL_get_cipher(ssl));
MuestraCertificados(ssl); /* muestra los certificados */
printf("\nMANDAS: ");
scanf("%s", msg);
SSL_write(ssl, msg, strlen(msg)); /* encripta y manda el mensaje */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* obtiene respuesta y desencripta */
buf[bytes] = 0;
printf("RECIBO: %s\n", buf);
SSL_free(ssl); /* libera el estado de conexion */
}
close(server); /* cierra el socket */
SSL_CTX_free(ctx); /* libera el contexto */
return 0;
}
and the server code:
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "openssl/ssl.h"
#include "openssl/err.h"
int CreaEscuchador(int port){
int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
perror("no se puede vincular puerto");
abort();
}
if ( listen(sd, 10) != 0 )
{
perror("no se puede configurar puerto de escucha");
abort();
}
return sd;
}
SSL_CTX* InitServerCTX(void){
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* carga y registra todos los cifrados etc */
SSL_load_error_strings(); /* carga todos los mensajes de error */
ctx = SSL_CTX_new(SSLv23_server_method()); /* crea un contexto para la instancia del servidor */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void CargarCertificados(SSL_CTX* ctx, char* CertFile, char* KeyFile){
if (SSL_CTX_load_verify_locations(ctx, CertFile, KeyFile) != 1)
ERR_print_errors_fp(stderr);
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
ERR_print_errors_fp(stderr);
/* setea el certificado local a traves de CertFile */
if (SSL_CTX_use_certificate_file(ctx, CertFile , SSL_FILETYPE_PEM) <= 0){
ERR_print_errors_fp(stderr);
abort();
}
/* setea la clave privada a traves de keyFile (puede ser igual que certfile) */
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0){
ERR_print_errors_fp(stderr);
abort();
}
/* verifica la clave privada */
if (!SSL_CTX_check_private_key(ctx)){
fprintf(stderr, "la clave privada no coincide con el certificado publico\n");
abort();
}
//fuerza al cliente a tener un certificado
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
void MuestraCertificados(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* obtiene los certificados */
if ( cert != NULL )
{
printf("CERTIFICADOS EN EL SERVIDOR:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("SUJETO: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("EMISOR: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("NO EXISTEN CERTIFICADOS.\n");
}
void Servlet(SSL* ssl) /* servicio del servidor */
{ char buf[1024];
char reply[1024];
int sd, bytes;
const char* echo="%s\n";
if ( SSL_accept(ssl) < 0 ) /* hace el accept ssl */
ERR_print_errors_fp(stderr);
else
{
MuestraCertificados(ssl); /* muestra los certificados */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* recibe el mensaje */
if ( bytes > 0 )
{
buf[bytes] = 0;
printf("RECIBO: %s\n", buf);
sprintf(reply, echo, buf); /* contruyo la respuesta */
SSL_write(ssl, reply, strlen(reply)); /* mando la respuesta */
}
else
ERR_print_errors_fp(stderr);
}
sd = SSL_get_fd(ssl); /* coge la conexion del socket */
SSL_free(ssl); /* libera la conexion ssl */
close(sd); /* cierra la conexion */
}
int main(int argc, char*argv[])
{ SSL_CTX *ctx;
int server;
if(argc != 4)
return -1;
SSL_library_init();
ctx = InitServerCTX(); /* inicilaiza SSL */
CargarCertificados(ctx, argv[2], argv[3]); /* carga los certificados */
server = CreaEscuchador(atoi(argv[1])); /* crea el escuchador */
while (1){
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;
int client = accept(server, (struct sockaddr*)&addr, &len); /* acepta la conexion como siempre */
printf("CONEXION: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
ssl = SSL_new(ctx); /* coge le nuevo estado ssl con el contexto */
SSL_set_fd(ssl, client); /* setea el socket a conexion ssl */
Servlet(ssl); /* servicio del servidor */
}
close(server); /* cierra el socket */
SSL_CTX_free(ctx); /* libera el contexto */
}
to generate my certificates I use a root certificate:
openssl genrsa -out rootkey.pem 2048
openssl req -new -x509 -key rootkey.pem -out rootcert.pem
then I create a client and a server cert:
openssl genrsa -out clientkey.pem 2048
openssl req -new -key clientkey.pem -out client_solicit.csr
openssl x509 -req -CAcreateserial -in client_solicit.csr -CA rootcert.pem -CAkey rootkey.pem -out clientcert.pem
//same lines to server cert
done! the arguments of SSL_CTX_load_verify_locations were wrong. The cert file points to a file of CA certificates in PEM format.
SSL_CTX_load_verify_locations(ctx, "rootcert.pem", "rootkey.pem") != 1)