Il y a quelques temps, j'ai pondu un code minuscule et truffé de failles qui avait pour objectif de récupérer des informations basiques sur un petit routeur Wifi, La Fonera. Le challenge, c'est que ce code devait être le plus concis possible car le petit routeur ne dispose que de 4Mo de flash et 16Mo de RAM. Voici ce à quoi cela a abouti :

ATTENTION: ce code est truffé de chemins hardcodés et j'y ai inclu très peu de verifications, il ne s'agit aucunement d'un exemple à suivre à la lettre mais d'une simple petite CLI sans prétention :

Ce code est protégé par la GPL, en effet, le routeur en question étant commercialisé par une société, j'ai préféré me prémunir d'une utilisation abusive, meme si FON respecte scrupuleusement la GPL depuis ses débuts

/*
 * infon, a simple information server for the Fonera Access Point
 * Copyright (C) 2007, Emile "iMil" Heitor
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Emile "iMil" Heitor <imil@gcu.info>
 * G.C.U. - 2007
 */

Quelques includes, mais le strict minimum :

#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

Suivi de quelques constantes habituelles

#define TCP_PORT 1702
#define BUFLEN 2048

Ici, la fonction wait3(2) garantit de ne pas laisser trainer de processus zombies à la sortie d'un fork(2)

/* Zombie handling */
void
resident(int signo)
{
    union wait wstatus;

    while(wait3((int *)&wstatus, WNOHANG, (struct rusage *)NULL) > 0);
}

Une petite “helper function”, ici remplacement d'un caractere par \0 :

/* Replace a char with \0 */
void
ctoeol(char *str, char c)
{
    for(;*str != '\0'; str++)
        if (*str == c) {
            *str = '\0';
            return;
        }
}

Deux petites extensions de la précédente, remplacement des espaces et des retour chariot par \0

void
sptoeol(char *str)
{
    ctoeol(str, ' ');
}
void
crtoeol(char *str)
{
    ctoeol(str, '\n');
}

Quick'n'dirty, cette fonction retourne la valeur d'une colonne fonction du séparateur

/* Extract column value */
char *
getcolval(char *raw, const char *token, int colnum)
{
    char *p, *ep;
    int i;

    if ((p = strstr(raw, token)) == NULL)
        return NULL;

    for (i = 0; i < colnum; i++) {
        p += strlen(token);
        p++;
        /* there's spaces between identifier and data */
        if (*p == ' ')
            for (; *p == ' '; p++);
    }

    p = strdup(p);
    ep = p;
    sptoeol(ep);

    return p;
}

Cette fonction hautement insecure renvoie le retour de l'execution d'uen commande

/* For future use */
char *
fonexec(const char *cmd)
{
    FILE *fp;
    char buf[BUFLEN];

    if ((fp = popen(cmd, "r")) == NULL)
        return NULL;
    else {
        (void)fgets(buf, BUFLEN, fp);
        pclose(fp);
        crtoeol(&buf[0]);

        return strdup(buf);
    }
}

Cette fonction lit simplement le fichier passé en parametre et retourne son contenu ( ⇐ BUFLEN ) sous forme de char *

char *
procread(const char *entry)
{
    int fd;
    char buf[BUFLEN]="";

    if ((fd = open(entry, O_RDONLY)) < 0)
        return NULL;
    else {
        read(fd, buf, BUFLEN);
        close(fd);
        return strdup(buf);
    }
}

Cette fonction hautement importable utilise les fonctions précédentes pour ecrire sur fd les statistiques d'utilisation memoire fonction de la lecture de /proc

void
meminfo(int fd)
{
    char *mem, *total, *freem, out[BUFLEN] = "";

    if ((mem = procread("/proc/meminfo")) == NULL)
        write(fd, "could not read /proc/meminfo\n", BUFLEN);
    else {
        total = getcolval(mem, "MemTotal:", 1);
        freem = getcolval(mem, "MemFree:", 1);

        snprintf(out, BUFLEN, "%s / %s", total, freem);

        write(fd, out, strlen(out));

        free(total); free(freem); free(mem);
    }
}

Cette fonction hautement importable renvoie sur fd le load average

void
loadavg(int fd)
{
    char *load;

    if ((load = procread("/proc/loadavg")) == NULL)
        write(fd, "could not read /proc/loadavg\n", BUFLEN);
    else {
        sptoeol(load);
        write(fd, load, strlen(load));
        free(load);
    }
}

Cette fonction hautement importable renvoie sur fd la qualité du lien wireless

void
linkq(int fd)
{
    char *lnq, *ath0, *ath1, out[BUFLEN] = "";

    if ((lnq = procread("/proc/net/wireless")) == NULL)
        write(fd, "could not read /proc/net/wireless\n", BUFLEN);
    else {
        ath0 = getcolval(lnq, "ath0:", 2);
        ath1 = getcolval(lnq, "ath1:", 2);

        snprintf(out, BUFLEN, "%s / %s", ath0, ath1);

        write(fd, out, strlen(out));

        free(ath0); free(ath1); free(lnq);
    }
}

Cette fonction hautement importable renvoie sur fd la quantité de données transmise et recue

void
gettxrx(int fd)
{
    char *procnetdev, *ath0tx, *ath0rx, *ath1tx, *ath1rx, out[BUFLEN] = "";

    if ((procnetdev = procread("/proc/net/dev")) == NULL)
        write(fd, "could not read /proc/net/dev\n", BUFLEN);
    else {
        ath0rx = getcolval(procnetdev, "wifi0:", 1);
        ath1rx = getcolval(procnetdev, "ath1:", 1);
        ath0tx = getcolval(procnetdev, "wifi0:", 9);
        ath1tx = getcolval(procnetdev, "ath1:", 9);

        snprintf(out, BUFLEN, "%s / %s | %s / %s",
             ath0rx, ath0tx, ath1rx, ath1tx);

        write(fd, out, strlen(out));

        free(ath0rx); free(ath1rx);
        free(ath0tx); free(ath1tx);
        free(procnetdev);
    }
}

Exemple d'utilisation de fonexec()

void
foocommand(int fd)
{
    char *cmd = "/bin/date", *res;

    res = fonexec(cmd);
    write(fd, res, strlen(res));
    free(res);
}

Simple fonction d'aide

void
help(int fd)
{
    const char *h = "l: load average\nm: memory info (total / free)\nk: link quality (ath0 / ath1)\nt: ath0 rx / ath0 tx | ath1 rx / ath1 tx\nq: exit console";

    write(fd, h, strlen(h));
}

Fonction de gestion des services

void
service(int fd)
{
    char buf[BUFLEN] = "";

    write(fd, "type h for help\nfonera> ", 25);
    for (;;) {

        read(fd, buf, BUFLEN);
        switch(buf[0]) {
        case 'l':
            loadavg(fd);
            break;
        case 'm':
            meminfo(fd);
            break;
        case 'h':
            help(fd);
            break;
        case 'k':
            linkq(fd);
            break;
        case 't':
            gettxrx(fd);
            break;
        case 'x':
            foocommand(fd);
            break;
        case 'q':
            close(fd);
            return;
            break;
        case '\0':
            break;
        default:
            write(fd, "FON!", 5);
            break;
        }
        if (buf[0] != '\0')
            write(fd, "\nfonera> ", 9);

        buf[0] = '\0';
    }
}

Et finalement notre main() qui démarre le serveur TCP et renvoie le fd sur service()

int
main(int argc, char *argv[])
{
    int srv_fd, clt_fd, sin_size, pid, yes=1;
    struct sockaddr_in clt_addr, local_addr;
    struct timeval timeout = {5, 0};
    struct linger lng = {1, 5};

    if ( (srv_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        perror("server: can't open stream socket");

    local_addr.sin_family= AF_INET;
    local_addr.sin_addr.s_addr= htonl(INADDR_ANY);
    local_addr.sin_port= htons(TCP_PORT);
    memset(&(local_addr.sin_zero), 0, 8);

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

    setsockopt(srv_fd, SOL_SOCKET, SO_RCVTIMEO,
           &timeout, sizeof(struct timeval));
    setsockopt(srv_fd, SOL_SOCKET, SO_SNDTIMEO,
           &timeout, sizeof(struct timeval));
    setsockopt(srv_fd, SOL_SOCKET, SO_LINGER,
           &lng, sizeof(struct linger));

    if (bind(srv_fd, (struct sockaddr *) &local_addr,
         sizeof(local_addr)) < 0) {
        perror("server: can't bind local address");
        exit(1);
    }

    if (listen(srv_fd, 5) < 0) {
        perror("listen");
        exit(1);
    }

    signal(SIGCHLD, resident);

    for ( ;; ) {
        sin_size = sizeof(struct sockaddr_in);

        if ((clt_fd = accept(srv_fd,
                     (struct sockaddr *)&clt_addr,
                     (socklen_t *)&sin_size)) < 0) {
            continue;
        }

        pid=fork();
        switch(pid) {
        case -1:
            close(srv_fd);
            perror("fork");
            exit(1);
        case 0:
            close(srv_fd);
            service(clt_fd);
            exit(0);
        }
        }
        close(clt_fd);

    }
}
c/infon.txt · Last modified: 2010/01/12 13:29 (external edit)
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0