// Emacs style mode select -*- C++ -*-
//------------------------------------------------------------------------
//
// Copyright(c) 2001, 2002 Simon Howard
//
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//--------------------------------------------------------------------------
//
// Unix Finger
//
// Connect to finger port and get list of users
//
//--------------------------------------------------------------------------

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

#include "parse.h"
#include "scan.h"
#include "unix.h"

char *findmask = "*";

//
// parse response from fingerd
//

static void unix_parse_finger_line(int machine, char *line)
{
        while(*line == ' ')
                line++;
        
        if(!*line || *line == '\n')
                return;
        
        // ignore certain lines
        // 'no one'  -- no one logged in
        // 'login' -- start of description column
        // lines which start with [ are the individual cluster nodes
        
        if(strncasecmp(line, "no one", 6) &&
           strncasecmp(line, "login", 5) &&
           line[0] != '[') {
                char *s;
                
                s = strchr(line, ' ');
                
                if(s) {
                        char *username = malloc(s - line + 2);
                        strncpy(username, line, s-line);
                        username[s-line] = '\0';
                        
                        // send this information back to the
                        // parent process
                        
                        scan_add_user(machine, username);
                }
        }
}

//
// finger a machine
//

void unix_finger_machine(int machine)
{
        struct sockaddr_in sa;
        struct hostent *hp;
        struct servent *service;
        int sock;
        char *hostname = machines[machine]->hostname;
     
        // check this IS a unix box

        if(machines[machine]->type != MACHINE_UNIX &&
           machines[machine]->type != MACHINE_LINUX) {
                fprintf(stderr, 
                        "%s isnt a unix box(this should never happen)\n",
                        hostname);
                return;
        }
     
        // find host ip
     
        hp = gethostbyname(hostname);
        
        if(!hp) {
                fprintf(stderr,
                        "unix_finger_machine: unable to resolve '%s'\n", 
                        hostname);
                scan_set_status(machine, STATUS_FAULT);
                return;
        }
     
        // find port number
        
        service = getservbyname("finger", "tcp");
        
        //endservent();
        
        if(!service) {
                fprintf(stderr, "finger service not found!\n");
                exit(-1);
        }
        
        // create sockaddr
        
        memset(&sa,0,sizeof(sa));
        
        memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length);
        sa.sin_family = hp->h_addrtype;
        sa.sin_port = service->s_port;
        
        // create socket
        
        sock = socket(hp->h_addrtype, SOCK_STREAM, 0);
     
        if (sock < 0) {
                fprintf(stderr, "cant create socket");
                perror("finger_machine");
                scan_set_status(machine, STATUS_FAULT);
                return;
        }
        
        // connect
        
        if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
                fprintf(stderr,
                        "unix_finger_machine: cant connect to %s (%s)\n",
                        hostname,
                        strerror(errno)); 

                // catch certain types of error:
                // if we get ECONNREFUSED, then there is obviously 
                // a machine up refusing the connections
                // possibly the fingerd has just died or something

                if(errno == ECONNREFUSED) {

                        // up but unknown

                        scan_set_status(machine, STATUS_FAULT);
                } else {
                
                        // mark machine as down
                
                        scan_set_status(machine, STATUS_DOWN);
                }
        } else {

                const char *message = "\n";
                                
                //
                // connected to machine
                //

		scan_set_status(machine, STATUS_UP);

		//
                // send a newline to get it to list all users
                // add all users listed to list
                //
                
                if(write(sock, message, strlen(message)) < 0) {
                        fprintf(stderr, "error writing to socket\n");
                        perror("finger_machine");
                } else {
                        
                        // get response
#define READBUF_SIZE 8192
                        char readbuf[READBUF_SIZE];
                        int b;
                        
                        // read from socket
                        
                        b = read(sock, readbuf, READBUF_SIZE-1);
                        
                        if(b < 0) {
                                fprintf(stderr, "error reading from socket\n");
                                perror("finger_machine");
                        } else {
                                
                                char *p = readbuf;
                                
                                readbuf[b] = '\0';
                                
                                // cheap hack: convert all \rs to \ns
                                
                                {
                                        char *s;
                                        for(s = readbuf; *s; s++)
                                                if(*s == '\r')
                                                        *s = '\n';
                                }
                                
                                while(1) {
                                        unix_parse_finger_line(machine, p);
                                        
                                        p = strchr(p, '\n');
                                        if(!p)
                                                break;
                                        else
                                                p++;
                                }
                        }      
                }
        }
        
        close(sock);
}
