====== Jednoduchá adresářová služba ======
===== Popis použitého aplikačního protokolu =====
Protokol adresářové služby je velice triviální a inspiroval se fenoménem dnešního internetu -- lolcats(([[http://icanhascheezburger.com/|Lolcats ‘n’ Funny Pictures – I Can Has Cheezburger?]], [[http://speaklolspeak.com|The Definitive Lolcats Glossary – Speak Lol Speak]])). Nejlépe bude předvést si jej na ukázce:
^ ./server 10000 ^ ./client -h localhost -p 10000 -NSF -l xkalab00 ^
| | ICANHAS NSF WHERE LOGIN xkalab00 KTHX |
| Jan;Kalab;FIT;\r\n | |
| \r\n | |
Jak vidno, klient pouze zašle požadavek začínající klíčovým slovem ICANHAS. Následuje seznam sloupců, které klient požaduje (NSF). Další je klíčové slovo WHERE a po něm následujicí dvojice klíčové slovo pro vyhledání (NAME, SURNAME, LOGIN, FACULTY) a hodnota. Požavek ukončuje klíčové slovo KTHX.
Tento požadavek server zpracuje, a začne vracet řádky adresáře. Nakonec zprávu uzavře znaky \r\n, čímž vznikne čtvěřice \r\n\r\n na kterou klient zareguje ukončením spojení a vypsáním přijatého adresáře.
===== README =====
+------------------------------+
| Jednoducha adresarova sluzba |
+------------------------------+
Autor: Jan Kalab
SERVER
------
Server spustite prikazem ./server xxx kde xxx znaci cislo portu na kterem bude
server poslouchat. Toto cislo musi byt vetsi nez 1024. Na jakekoliv chyby Vas
server upozorni. Server ocekava v adresari ze ktereho byl spusten soubor
ipk_database.txt ze ktereho muze cist. V tomto souboru musi byt stredniky
oddeleny seznam jmen, prijmeni, loginu a fakult ve stejnem poradi a formatu
v jakem bylo vzorove zadani (vcetne ukoncovani radku pomoci CRLF). Pokud toto
nebude dodrzeno, neni zarucena funkcnost serveru Server nelze ukoncit jinak nez
zaslanim patricneho signalu (nejlepe 15 SIGTERM).
KLIENT
------
Klient se spousti prikazem ./client -h xxx -p yyy kde xxx je adresa na kterou se
ma klient pripojit a yyy cislo portu na ktery se ma pripojit. Cislo portu musi
byt vetsi nez 1024, ale na to vas klient upozorni. Dale by mely nasledovat
nektere z vyhledavacich parametru -n -s -l -f pro jmeno, prijmeni, login
a fakultu. Pokud nebude zadano zadne vyhledavaci kriterium, dojde i tak
k odeslani dotazu na server, ktery vrati prazdnou (ci nesmyslnou) odpoved. Dale
je mozno zadat ktere sloupce chcete vratit, k tomu slouzi parametr -NSLF,
u ktereho lze nektera pismena vynechat. Neni-li tento parametr zadan vubec,
budou vypsany vsechny sloupce. Po spusteni klienta s patricnymi parametry se
spoji se serverem a na standardni vystup vypise pozadovana data z adresare.
===== server.c =====
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "D'oh! Spatny pocet parametru! Ocekava se jeden, a to cislo portu.\n");
return 1;
}
unsigned int port = atoi(argv[1]);
if (port <= 1024) {
fprintf(stderr, "D'oh! Na vyhrazenem portu odmitam bezet!\n");
return 1;
}
FILE *file;
file = fopen("ipk_database.txt", "r");
if (file == NULL) {
fprintf(stderr, "D'oh! Nedokazu otevrit soubor ipk_database.txt!\n");
return 1;
}
if (fseek(file, 0, SEEK_END)) {
fprintf(stderr, "D'oh! Nedokazu zjistit velikost souboru ipk_database.txt!\n");
return 1;
}
unsigned long filesize = ftell(file);
fseek(file, 0, SEEK_SET);
char* database = malloc(filesize * sizeof(char));
if (database == NULL) {
fprintf(stderr, "D'oh! Chyba alokace pameti!\n");
return 1;
}
unsigned int items = 0;
for (unsigned long i = 0; i < filesize; i++) {
database[i] = fgetc(file);
if (database[i] == '\n') {
items++;
}
}
fclose(file);
char *line[items];
char *login[items];
char *name[items];
char *surname[items];
char *faculty[items];
line[0] = strtok(database, ";");
login[0] = strtok(NULL, ";");
surname[0] = strtok(NULL, ";");
name[0] = strtok(NULL, ";");
faculty[0] = strtok(NULL, ";\r\n");
for (unsigned int i = 1; i < items; i++) {
line[i] = strtok(NULL, ";");
login[i] = strtok(NULL, ";");
surname[i] = strtok(NULL, ";");
name[i] = strtok(NULL, ";");
faculty[i] = strtok(NULL, ";\r\n");
}
struct sockaddr_in myaddr;
struct sockaddr_in remoteaddr;
int listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener == -1) {
fprintf(stderr, "D'oh! Rozbita zasuvka!");
return 1;
}
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
myaddr.sin_port = htons(port);
memset(&(myaddr.sin_zero), '\0', 8);
if (bind(listener, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1) {
fprintf(stderr, "D'oh! Nepovedlo se zasunout zasuvku!\n");
return 1;
}
if (listen(listener, 10) == -1) {
fprintf(stderr, "D'oh! Server je nahluchly.\n");
return 1;
}
printf("Server @ %d\n", port);
socklen_t addrlen = sizeof(listener);
signal(SIGCHLD, SIG_IGN);
while (1) {
int newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);
if (newfd == -1) {
fprintf(stderr, "D'oh! Neprijimame...\n");
} else {
int pid = fork();
if (pid == 0) {
//printf("INCOMING!\n");
//cteni
unsigned int allocated = 1;
unsigned int dumped = 0;
char* request = malloc(allocated * sizeof(char));
while (!strstr(request, "KTHX")) {
read(newfd, request + dumped++, 1);
if (dumped >= allocated) {
allocated *= 2;
request = realloc(request, allocated);
if (!request) {
fprintf(stderr, "D'OH! Realloc fail, buy me more memory!\n");
return 1;
}
}
}
//printf("%s\n", request);
char* columns = strstr(request, "ICANHAS ") + strlen("ICANHAS ");
columns = strtok(columns, " ");
//printf("\n%s\n", columns);
char* filters = columns + strlen(columns) + strlen(" WHERE ");
//printf("%s", filters);
char* filter[10];
filter[0] = strtok(filters, " ");
//printf("Filter0: %s\n", filter[0]);
if (strstr(filter[0], "KTHX")) {
fprintf(stderr, "D'oh! Zadny vyhledavaci filtr\n");
write(newfd, "\r\n\r\n", 4);
free(request);
exit(1);
}
for (short i = 1; i < 10; i++) {
filter[i] = strtok(NULL, " ");
if (strstr(filter[i], "KTHX")) {
//printf("Konec filtru\n");
break;
}
}
char* namefilter = NULL;
char* surnamefilter = NULL;
char* loginfilter = NULL;
char* facultyfilter = NULL;
for (short i = 0; i < 10; i++) {
if (strstr(filter[i], "KTHX")) {
//printf("Konec filtru\n");
break;
}
if (!strcmp(filter[i], "NAME")) {
namefilter = filter[++i];
continue;
}
if (!strcmp(filter[i], "SURNAME")) {
surnamefilter = filter[++i];
continue;
}
if (!strcmp(filter[i], "LOGIN")) {
loginfilter = filter[++i];
continue;
}
if (!strcmp(filter[i], "FACULTY")) {
facultyfilter = filter[++i];
continue;
}
}
/*
printf("Columns: %s\n", columns);
printf("Name: %s\n", namefilter);
printf("Surname: %s\n", surnamefilter);
printf("Login: %s\n", loginfilter);
printf("Faculty: %s\n", facultyfilter);
*/
//Filtrovani
short mask[items];
for (unsigned int i = 0; i < items; i++) {
mask[i] = 1;
}
for (unsigned int i = 0; i < items; i++) {
if (mask[i] == 1) {
if (namefilter) {
if (strcmp(name[i], namefilter)) {
mask[i] = 0;
}
}
if (surnamefilter) {
if (strcmp(surname[i], surnamefilter)) {
mask[i] = 0;
}
}
if (loginfilter) {
if (strcmp(login[i], loginfilter)) {
mask[i] = 0;
}
}
if (facultyfilter) {
if (strcmp(faculty[i], facultyfilter)) {
mask[i] = 0;
}
}
}
}
//Odpoved
for (unsigned int i = 0; i < items; i++) {
if (mask[i] == 1) {
if (strstr(columns, "N")) {
//printf("N");
write(newfd, name[i], strlen(name[i]));
write(newfd, ";", 1);
}
if (strstr(columns, "S")) {
//printf("S");
write(newfd, surname[i], strlen(surname[i]));
write(newfd, ";", 1);
}
if (strstr(columns, "L")) {
//printf("L");
write(newfd, login[i], strlen(login[i]));
write(newfd, ";", 1);
}
if (strstr(columns, "F")) {
//printf("F");
write(newfd, faculty[i], strlen(faculty[i]));
write(newfd, ";", 1);
}
//printf("\n");
write(newfd, "\r\n", 2);
}
}
write(newfd, "\r\n", 2);
/*
write(newfd, name[0], strlen(name[0]));
write(newfd, ";", 1);
write(newfd, surname[0], strlen(surname[0]));
write(newfd, ";", 1);
write(newfd, login[0], strlen(login[0]));
write(newfd, ";", 1);
write(newfd, faculty[0], strlen(faculty[0]));
write(newfd, ";", 1);
write(newfd, "\r\n", 2);
*/
//printf("\n");
write(newfd, "\r\n\r\n", 4);
free(request);
exit(0);
}
}
}
/*
printf("%s, %s, %s, %s, %s\n", line[0], login[0], name[0], surname[0], faculty[0]);
printf("%s, %s, %s, %s, %s\n", line[items-1], login[items-1], name[items-1], surname[items-1], faculty[items-1]);
printf("Polozek: %d\n", items);
*/
free(database);
return 0;
}
===== client.c =====
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char* argv[]) {
if (argc < 5) {
fprintf(stderr, "D'oh! Prilis malo parametru! Chce to aspon -h host -p port\n");
return 1;
}
//Init
char* host = NULL;
unsigned int port = 0;
char* columns = NULL;
char* name = NULL;
char* surname = NULL;
char* login = NULL;
char* faculty = NULL;
//Parsovani vstupu
for (unsigned short i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0) {
host = argv[++i];
continue;
}
if (strcmp(argv[i], "-p") == 0) {
port = atoi(argv[++i]);
continue;
}
if (strstr(argv[i], "-N") || strstr(argv[i], "-S") || strstr(argv[i], "-L") || strstr(argv[i], "-F")) {
columns = argv[i] + 1;
continue;
}
if (strcmp(argv[i], "-n") == 0) {
name = argv[++i];
continue;
}
if (strcmp(argv[i], "-s") == 0) {
surname = argv[++i];
continue;
}
if (strcmp(argv[i], "-l") == 0) {
login = argv[++i];
continue;
}
if (strcmp(argv[i], "-f") == 0) {
faculty = argv[++i];
continue;
}
fprintf(stderr, "D'oh! Neznamy parametr %s!\n", argv[i]);
return 1;
}
//Mozne chyby
if (host == NULL) {
fprintf(stderr, "D'oh! Nerekl jsi mi kam se pripojit!\n");
return 1;
}
if (port <= 1024) {
fprintf(stderr, "D'oh! Na vyhrazenem (nebo nezadanem) portu odmitam bezet!\n");
return 1;
}
if (columns != NULL) {
if (strlen(columns) > 4) {
fprintf(stderr, "D'oh! Prilis mnoho sloupcu!\n");
return 1;
} else {
for (unsigned short i = 0; i < 4; i++) {
if (columns[i] == '\0') {
break;
}
if (columns[i] != 'N' && columns[i] != 'S' && columns[i] != 'L' && columns[i] != 'F') {
fprintf(stderr, "D'oh! Spatne definovane sloupce!\n");
return 1;
}
}
}
} else {
columns = "NSLF";
}
//Vypis
/*
printf("Client @ %s:%d\n", host, port);
printf("Columns: %s\n", columns);
printf("Name: %s\n", name);
printf("Surname: %s\n", surname);
printf("Login: %s\n", login);
printf("Faculty: %s\n", faculty);
*/
int socketfd = socket(PF_INET, SOCK_STREAM, 0);
if (socketfd == -1) {
fprintf(stderr, "D'oh! Rozbita zasuvka!\n");
return 1;
}
struct sockaddr_in stSockAddr;
bzero(&stSockAddr, sizeof(stSockAddr));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(port);
struct hostent *hoststruct;
if ((hoststruct = gethostbyname(host)) == NULL) {
fprintf(stderr, "D'OH! Unknown host.\n");
return 1;
}
memcpy(&stSockAddr.sin_addr, hoststruct->h_addr, hoststruct->h_length); //adresa
if (connect(socketfd, (struct sockaddr*) &stSockAddr, sizeof(stSockAddr)) == -1) {
fprintf(stderr, "D'oh! Server is dead!\n");
return 1;
}
//pozadavek
unsigned int filters = 0;
if (name) {
filters += strlen(" NAME ") + strlen(name);
}
if (surname) {
filters += strlen(" SURNAME ") + strlen(surname);
}
if (login) {
filters += strlen(" LOGIN ") + strlen(login);
}
if (faculty) {
filters += strlen(" FACULTY ") + strlen(faculty);
}
char* request = malloc((strlen("ICANHAS ") + strlen(columns) + strlen(" WHERE") + filters + strlen(" KTHX")) * sizeof(char));
if (!request) {
fprintf(stderr, "D'OH! Malloc fail, buy more RAM!\n");
return 1;
}
request[0] = '\0';
strcat(request, "ICANHAS ");
strcat(request, columns);
strcat(request, " WHERE");
if (name) {
strcat(request, " NAME ");
strcat(request, name);
}
if (surname) {
strcat(request, " SURNAME ");
strcat(request, surname);
}
if (login) {
strcat(request, " LOGIN ");
strcat(request, login);
}
if (faculty) {
strcat(request, " FACULTY ");
strcat(request, faculty);
}
strcat(request, " KTHX");
//printf("Request: %s\n", request);
if (write(socketfd, request, strlen(request)) <= 0) {
fprintf(stderr, "D'oh! Nedokazu psat do zasuvky!\n");
return 1;
}
free(request);
//Odpoved
//printf("Answer:\n");
unsigned int allocated = 1;
unsigned int dumped = 0;
char* answer = malloc(allocated * sizeof(char));
while (!strstr(answer, "\r\n\r\n")) {
read(socketfd, answer + dumped++, 1);
if (dumped >= allocated) {
allocated *= 2;
answer = realloc(answer, allocated);
if (answer == NULL) {
fprintf(stderr, "D'OH! Realloc fail, buy me more memory!\n");
return 1;
}
}
}
printf("%s", answer);
free(answer);
//printf("Everything went just fine.\n");
shutdown(socketfd, 2);
close(socketfd);
return 0;
}