/*
 *  Copyright (c) 2009-2020, Peter Haag
 *  Copyright (c) 2008, SWITCH - Teleinformatikdienste fuer Lehre und Forschung
 *  All rights reserved.
 *  
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions are met:
 *  
 *   * Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice, 
 *     this list of conditions and the following disclaimer in the documentation 
 *     and/or other materials provided with the distribution.
 *   * Neither the name of the author nor the names of its contributors may be 
 *     used to endorse or promote products derived from this software without 
 *     specific prior written permission.
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 *  POSSIBILITY OF SUCH DAMAGE.
 *  
 */

static inline FlowSource_t *GetFlowSource(struct sockaddr_storage *ss) {
FlowSource_t	*fs;
void			*ptr;
ip_addr_t		ip;
in_port_t		port;
char			as[100];

    union {
        struct sockaddr_storage	*ss;
        struct sockaddr			*sa;
        struct sockaddr_in		*sa_in;
        struct sockaddr_in6		*sa_in6;
    } u;
    u.ss = ss;

	switch (ss->ss_family) {
		case PF_INET: {
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
			if (ss->ss_len != sizeof(struct sockaddr_in) ) {
				// malformed struct
				LogError("Malformed IPv4 socket struct in '%s', line '%d'", __FILE__, __LINE__ );
				return NULL;
			}
#endif
			ip.V6[0] = 0;
			ip.V6[1] = 0;
			ip.V4	 = ntohl(u.sa_in->sin_addr.s_addr);
			port	 = u.sa_in->sin_port;
			ptr 	 = &u.sa_in->sin_addr;
			} break;
		case PF_INET6: {
			uint64_t *ip_ptr = (uint64_t *)u.sa_in6->sin6_addr.s6_addr;
			
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
			if (ss->ss_len != sizeof(struct sockaddr_in6) ) {
				// malformed struct
				LogError("Malformed IPv6 socket struct in '%s', line '%d'", __FILE__, __LINE__ );
				return NULL;
			}
#endif
			// ptr = &((struct sockaddr_in6 *)sa)->sin6_addr;
			ip.V6[0] = ntohll(ip_ptr[0]);
			ip.V6[1] = ntohll(ip_ptr[1]);
			port	 = u.sa_in6->sin6_port;
			ptr		 = &u.sa_in6->sin6_addr;
			} break;
		default:
			// keep compiler happy
			ip.V6[0] = 0;
			ip.V6[1] = 0;
			port	 = 0;
			ptr		 = NULL;

			LogError("Unknown sa fanily: %d in '%s', line '%d'", ss->ss_family, __FILE__, __LINE__ );
			return NULL;
	}

#ifdef DEVEL
	inet_ntop(ss->ss_family, ptr, as, sizeof(as));
	as[99] = '\0';
	printf("Flow Source IP: %s\n", as);
#endif

	fs = FlowSource;
	while ( fs ) {
		if ( ip.V6[0] ==  fs->ip.V6[0] && ip.V6[1] == fs->ip.V6[1] ) {
			fs->port = port;
			return fs;
		}

		// if we match any source, store the current IP address - works as faster cache next time
		// and identifies the current source by IP
		if ( fs->any_source ) {
			fs->ip	 = ip;
			fs->port = port;
			fs->sa_family = ss->ss_family;
			return fs;
		}
		fs = fs->next;
	}

	if ( ptr ) {
		inet_ntop (ss->ss_family, ptr, as, 100);
	} else 
		strncpy(as, "<unknown>", 99);

	as[99] = '\0';
	LogError("Unknown flow source: '%s'" , as);

	return NULL;

} // End of GetFlowSource



