| /* netdb.cc: network database related routines. |
| |
| This file is part of Cygwin. |
| |
| This software is a copyrighted work licensed under the terms of the |
| Cygwin license. Please consult the file "CYGWIN_LICENSE" for |
| details. */ |
| |
| #include "winsup.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <netdb.h> |
| #include <shared_info.h> |
| |
| /* Locate and open a system network database file. relative_path |
| should be one of the following values: |
| "protocol" |
| "services" |
| "networks" |
| "hosts" |
| |
| It is the callers responsibility to close the file. */ |
| static FILE * |
| open_system_file (const char *relative_path) |
| { |
| /* system dir path is never longer. */ |
| char win32_name[MAX_PATH]; |
| |
| user_shared->warned_msdos = true; |
| sys_wcstombs (win32_name, MAX_PATH, windows_system_directory); |
| strcat (win32_name, "drivers\\etc\\"); |
| strcat (win32_name, relative_path); |
| FILE *result = fopen (win32_name, "rt"); |
| debug_printf ("handle to netdb file %s: %p", win32_name, result); |
| return result; |
| } |
| |
| inline static FILE * |
| open_protocol_file () |
| { |
| return open_system_file ("protocol"); |
| } |
| |
| /* Wrapper for open_system_file(), fixing the constant name |
| "services". Returns the open file. */ |
| inline static FILE * |
| open_services_file () |
| { |
| return open_system_file ("services"); |
| } |
| |
| /* Read an entire line up till the next \n character. Memory for the |
| line is dynamically allocated, and the caller must call free() to |
| deallocate it. When the end of file is reached, NULL is returned. */ |
| static char * |
| get_entire_line (FILE *fd) |
| { |
| static const int BUFF_SIZE = 1024; |
| struct line_fragment |
| { |
| char buffer[BUFF_SIZE]; |
| line_fragment *next; |
| }; |
| |
| line_fragment *fragment_list_head = NULL; |
| line_fragment *fragment = NULL; |
| int fragment_count = 0; |
| char *result; |
| |
| do |
| { |
| line_fragment *new_fragment = (line_fragment *) malloc (sizeof (line_fragment)); |
| paranoid_printf ("line fragment allocated %p", new_fragment); |
| if (!fragment_list_head) |
| fragment_list_head = new_fragment; |
| if (fragment) |
| fragment->next = new_fragment; |
| fragment = new_fragment; |
| fragment->next = NULL; |
| *fragment->buffer = '\0'; |
| result = fgets (fragment->buffer, BUFF_SIZE, fd); |
| ++fragment_count; |
| } |
| while (result && !strchr (fragment->buffer, '\n')); |
| |
| if (*fragment_list_head->buffer != '\0') |
| { |
| char *concatenated_line = (char *) calloc (fragment_count * BUFF_SIZE , sizeof (char)); |
| paranoid_printf ("concatenated line allocated %p", concatenated_line); |
| *concatenated_line = '\0'; |
| fragment = fragment_list_head; |
| while (fragment != NULL) |
| { |
| line_fragment *previous = fragment; |
| strcat (concatenated_line, fragment->buffer); |
| fragment = fragment->next; |
| free (previous); |
| } |
| return concatenated_line; |
| } |
| else |
| { |
| fragment = fragment_list_head; |
| while (fragment != NULL) |
| { |
| line_fragment *previous = fragment; |
| fragment = fragment->next; |
| free (previous); |
| } |
| return NULL; |
| } |
| } |
| |
| /* Characters representing whitespace. Used by parse_* routines to |
| delimit tokens. */ |
| static const char *SPACE = " \t\n\r\f"; |
| |
| /* Parse a list aliases from a network database file. Returns a |
| char** structure terminated by a NULL. */ |
| static void |
| parse_alias_list (char ***aliases, char **lasts) |
| { |
| struct alias_t |
| { |
| char *alias_name; |
| alias_t *next; |
| }; |
| alias_t *alias_list_head = NULL, *alias_list_tail = NULL; |
| char *alias; |
| int alias_count = 0; |
| alias = strtok_r (NULL, SPACE, lasts); |
| |
| while (alias) |
| { |
| ++alias_count; |
| alias_t *new_alias = (alias_t *) malloc (sizeof (alias_t)); |
| paranoid_printf ("new alias alloc %p", new_alias); |
| if (!alias_list_head) |
| alias_list_head = new_alias; |
| if (alias_list_tail) |
| alias_list_tail->next = new_alias; |
| new_alias->next = NULL; |
| new_alias->alias_name = alias; |
| alias_list_tail = new_alias; |
| alias = strtok_r (NULL, SPACE, lasts); |
| } |
| |
| *aliases = (char**) calloc (alias_count + 1, sizeof (char *)); |
| paranoid_printf ("aliases alloc %p", *aliases); |
| |
| char **current_entry = *aliases; |
| while (alias_list_head) |
| { |
| alias_t *previous = alias_list_head; |
| *current_entry = strdup (alias_list_head->alias_name); |
| paranoid_printf ("*current entry strdup %p", *current_entry); |
| alias_list_head = alias_list_head->next; |
| free (previous); |
| ++current_entry; |
| } |
| |
| *current_entry = NULL; |
| } |
| |
| /* Read the next line from svc_file, and parse it into the structure |
| pointed to by sep. sep can point to stack or static data, but it's |
| members will be overwritten with pointers to dynamically allocated |
| heap data accommodating parsed data. It is the responsibility of the |
| caller to free up the allocated structures. The function returns true |
| to indicate that a line was successfully read and parsed. False is |
| used to indicate that no more lines can be read and parsed. This |
| should also interpreted as end of file. */ |
| static bool |
| parse_services_line (FILE *svc_file, struct servent *sep) |
| { |
| char *line; |
| while ((line = get_entire_line (svc_file))) |
| { |
| char *name, *port, *protocol, *lasts; |
| |
| line[strcspn (line, "#")] = '\0'; // truncate at comment marker. |
| name = strtok_r (line, SPACE, &lasts); |
| if (!name) |
| { |
| free (line); |
| continue; |
| } |
| port = strtok_r (NULL, SPACE, &lasts); |
| protocol = strchr (port, '/'); |
| *protocol++ = '\0'; |
| sep->s_name = strdup (name); |
| paranoid_printf ("sep->s_name strdup %p", sep->s_name); |
| sep->s_port = htons (atoi (port)); |
| sep->s_proto = strdup (protocol); |
| paranoid_printf ("sep->s_proto strdup %p", sep->s_proto); |
| /* parse_alias_list relies on side effects. Read the comments |
| for that function.*/ |
| parse_alias_list (& sep->s_aliases, &lasts); |
| free (line); |
| return true; |
| } |
| return false; |
| } |
| |
| static FILE *svc_file = NULL; |
| static long int svc_read_pos = 0; |
| static struct servent current_servent; |
| |
| /* Steps through a struct servent, and frees all of the internal |
| structures.*/ |
| static void |
| free_servent (struct servent *sep) |
| { |
| free (sep->s_name); |
| free (sep->s_proto); |
| char ** current = sep->s_aliases; |
| while (current && *current) |
| { |
| free (*current); |
| ++current; |
| } |
| free (sep->s_aliases); |
| sep->s_name = NULL; |
| sep->s_port = 0; |
| sep->s_proto = NULL; |
| sep->s_aliases = NULL; |
| } |
| |
| extern "C" void |
| cygwin_setservent (int stay_open) |
| { |
| if (svc_file) |
| fclose (svc_file); |
| if (stay_open) |
| svc_file = open_services_file (); |
| free_servent (¤t_servent); |
| svc_read_pos = 0; |
| syscall_printf ("setservent (%d)", stay_open); |
| } |
| |
| extern "C" struct servent * |
| cygwin_getservent (void) |
| { |
| FILE *fd; |
| if (svc_file) |
| fd = svc_file; |
| else |
| { |
| fd = open_services_file (); |
| if (!fd) |
| { |
| syscall_printf ("%p = getservent()", NULL); |
| return NULL; |
| } |
| fseek (fd, svc_read_pos, SEEK_SET); |
| } |
| free_servent (¤t_servent); |
| bool found = parse_services_line (fd, ¤t_servent); |
| if (!svc_file) |
| { |
| svc_read_pos = ftell (fd); |
| fclose (fd); |
| } |
| struct servent *result; |
| if (found) |
| result = ¤t_servent; |
| else |
| result = NULL; |
| syscall_printf ("%p = getservent()", result); |
| return result; |
| } |
| |
| extern "C" void |
| cygwin_endservent (void) |
| { |
| if (svc_file) |
| { |
| fclose (svc_file); |
| svc_file = NULL; |
| } |
| free_servent (¤t_servent); |
| svc_read_pos = 0; |
| syscall_printf ("endservent ()"); |
| } |
| |
| /* Read the next line from proto_file, and parse it into the structure |
| pointed to by pep. pep can point to stack or static data, but it's |
| members will be overwritten with pointers to dynamically allocated |
| heap data accommodating parsed data. It is the responsibility of the |
| caller to free up the allocated structures. The function returns true |
| to indicate that a line was successfully read and parsed. False is |
| used to indicate that no more lines can be read and parsed. This |
| should also interpreted as end of file. */ |
| static bool |
| parse_protocol_line (FILE *proto_file, struct protoent *pep) |
| { |
| char *line; |
| while ((line = get_entire_line (proto_file))) |
| { |
| char *name, *protocol, *lasts; |
| |
| line[strcspn (line, "#")] = '\0'; // truncate at comment marker. |
| name = strtok_r (line, SPACE, &lasts); |
| if (!name) |
| { |
| free (line); |
| continue; |
| } |
| protocol = strtok_r (NULL, SPACE, &lasts); |
| pep->p_name = strdup (name); |
| paranoid_printf ("pep->p_name strdup %p", pep->p_name); |
| pep->p_proto = atoi (protocol); |
| /* parse_alias_list relies on side effects. Read the comments |
| for that function.*/ |
| parse_alias_list (& pep->p_aliases, &lasts); |
| free (line); |
| return true; |
| } |
| return false; |
| } |
| |
| static FILE *proto_file = NULL; |
| static long int proto_read_pos = 0; |
| static struct protoent current_protoent; |
| |
| /* Steps through a struct protoent, and frees all the internal |
| structures. */ |
| static void |
| free_protoent (struct protoent *pep) |
| { |
| free (pep->p_name); |
| char ** current = pep->p_aliases; |
| while (current && *current) |
| { |
| free (*current); |
| ++current; |
| } |
| free (pep->p_aliases); |
| pep->p_name = NULL; |
| pep->p_proto = 0; |
| pep->p_aliases = NULL; |
| } |
| |
| extern "C" void |
| cygwin_setprotoent (int stay_open) |
| { |
| if (proto_file) |
| fclose (proto_file); |
| |
| if (stay_open) |
| proto_file = open_protocol_file (); |
| |
| free_protoent (¤t_protoent); |
| proto_read_pos = 0; |
| syscall_printf ("setprotoent (%d)", stay_open); |
| } |
| |
| extern "C" struct protoent * |
| cygwin_getprotoent (void) |
| { |
| FILE *fd; |
| |
| if (proto_file) |
| fd = proto_file; |
| else |
| { |
| fd = open_protocol_file (); |
| if (!fd) |
| { |
| syscall_printf ("%p = getprotoent()", NULL); |
| return NULL; |
| } |
| fseek (fd, proto_read_pos, SEEK_SET); |
| } |
| free_protoent (¤t_protoent); |
| |
| bool found = parse_protocol_line (fd, ¤t_protoent); |
| if (!proto_file) |
| { |
| proto_read_pos = ftell (fd); |
| fclose (fd); |
| } |
| |
| struct protoent *result; |
| if (found) |
| result = ¤t_protoent; |
| else |
| result = NULL; |
| |
| syscall_printf ("%p = getprotoent()", result); |
| return result; |
| } |
| |
| extern "C" void |
| cygwin_endprotoent (void) |
| { |
| if (proto_file) |
| { |
| fclose (proto_file); |
| proto_file = NULL; |
| } |
| |
| free_protoent (¤t_protoent); |
| proto_read_pos = 0; |
| syscall_printf ("endprotoent ()"); |
| } |