| /* |
| * stunnel TLS offloading and load-balancing proxy |
| * Copyright (C) 1998-2015 Michal Trojnara <Michal.Trojnara@mirt.net> |
| * |
| * 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, see <http://www.gnu.org/licenses>. |
| * |
| * Linking stunnel statically or dynamically with other modules is making |
| * a combined work based on stunnel. Thus, the terms and conditions of |
| * the GNU General Public License cover the whole combination. |
| * |
| * In addition, as a special exception, the copyright holder of stunnel |
| * gives you permission to combine stunnel with free software programs or |
| * libraries that are released under the GNU LGPL and with code included |
| * in the standard release of OpenSSL under the OpenSSL License (or |
| * modified versions of such code, with unchanged license). You may copy |
| * and distribute such a system following the terms of the GNU GPL for |
| * stunnel and the licenses of the other code concerned. |
| * |
| * Note that people who make modified versions of stunnel are not obligated |
| * to grant this special exception for their modified versions; it is their |
| * choice whether to do so. The GNU General Public License gives permission |
| * to release a modified version without this exception; this exception |
| * also makes it possible to release a modified version which carries |
| * forward this exception. |
| */ |
| |
| #include "common.h" |
| #include "prototypes.h" |
| |
| #ifdef USE_WIN32 |
| |
| DISK_FILE *file_open(char *name, FILE_MODE mode) { |
| DISK_FILE *df; |
| LPTSTR tname; |
| HANDLE fh; |
| DWORD desired_access, creation_disposition; |
| |
| /* open file */ |
| switch(mode) { |
| case FILE_MODE_READ: |
| desired_access=GENERIC_READ; |
| creation_disposition=OPEN_EXISTING; |
| break; |
| case FILE_MODE_APPEND: |
| /* reportedly more compatible than FILE_APPEND_DATA */ |
| desired_access=GENERIC_WRITE; |
| creation_disposition=OPEN_ALWAYS; /* keep the data */ |
| break; |
| case FILE_MODE_OVERWRITE: |
| desired_access=GENERIC_WRITE; |
| creation_disposition=CREATE_ALWAYS; /* remove the data */ |
| break; |
| default: /* invalid mode */ |
| return NULL; |
| } |
| tname=str2tstr(name); |
| fh=CreateFile(tname, desired_access, FILE_SHARE_READ, NULL, |
| creation_disposition, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); |
| str_free(tname); /* str_free() overwrites GetLastError() value */ |
| if(fh==INVALID_HANDLE_VALUE) |
| return NULL; |
| if(mode==FILE_MODE_APPEND) /* workaround for FILE_APPEND_DATA */ |
| SetFilePointer(fh, 0, NULL, FILE_END); |
| |
| /* setup df structure */ |
| df=str_alloc(sizeof df); |
| df->fh=fh; |
| return df; |
| } |
| |
| #else /* USE_WIN32 */ |
| |
| DISK_FILE *file_fdopen(int fd) { |
| DISK_FILE *df; |
| |
| df=str_alloc(sizeof(DISK_FILE)); |
| df->fd=fd; |
| return df; |
| } |
| |
| DISK_FILE *file_open(char *name, FILE_MODE mode) { |
| DISK_FILE *df; |
| int fd, flags; |
| |
| /* open file */ |
| switch(mode) { |
| case FILE_MODE_READ: |
| flags=O_RDONLY; |
| break; |
| case FILE_MODE_APPEND: |
| flags=O_CREAT|O_WRONLY|O_APPEND; |
| break; |
| case FILE_MODE_OVERWRITE: |
| flags=O_CREAT|O_WRONLY|O_TRUNC; |
| break; |
| default: /* invalid mode */ |
| return NULL; |
| } |
| #ifdef O_NONBLOCK |
| flags|=O_NONBLOCK; |
| #elif defined O_NDELAY |
| flags|=O_NDELAY; |
| #endif |
| #ifdef O_CLOEXEC |
| flags|=O_CLOEXEC; |
| #endif /* O_CLOEXEC */ |
| fd=open(name, flags, 0640); |
| if(fd==INVALID_SOCKET) |
| return NULL; |
| |
| /* setup df structure */ |
| df=str_alloc(sizeof df); |
| df->fd=fd; |
| return df; |
| } |
| |
| #endif /* USE_WIN32 */ |
| |
| void file_close(DISK_FILE *df) { |
| if(!df) /* nothing to do */ |
| return; |
| #ifdef USE_WIN32 |
| CloseHandle(df->fh); |
| #else /* USE_WIN32 */ |
| if(df->fd>2) /* never close stdin/stdout/stder */ |
| close(df->fd); |
| #endif /* USE_WIN32 */ |
| str_free(df); |
| } |
| |
| ssize_t file_getline(DISK_FILE *df, char *line, int len) { |
| /* this version is really slow, but performance is not important here */ |
| /* (no buffering is implemented) */ |
| ssize_t i; |
| #ifdef USE_WIN32 |
| DWORD num; |
| #else /* USE_WIN32 */ |
| ssize_t num; |
| #endif /* USE_WIN32 */ |
| |
| if(!df) /* not opened */ |
| return -1; |
| |
| for(i=0; i<len-1; i++) { |
| #ifdef USE_WIN32 |
| ReadFile(df->fh, line+i, 1, &num, NULL); |
| #else /* USE_WIN32 */ |
| num=read(df->fd, line+i, 1); |
| #endif /* USE_WIN32 */ |
| if(num!=1) { /* EOF */ |
| if(i) /* any previously retrieved data */ |
| break; |
| else |
| return -1; |
| } |
| if(line[i]=='\n') /* LF */ |
| break; |
| if(line[i]=='\r') /* CR */ |
| --i; /* ignore - it must be the last check */ |
| } |
| line[i]='\0'; |
| return i; |
| } |
| |
| ssize_t file_putline(DISK_FILE *df, char *line) { |
| char *buff; |
| #ifdef USE_WIN32 |
| DWORD len, num; |
| #else /* USE_WIN32 */ |
| size_t len; |
| ssize_t num; |
| #endif /* USE_WIN32 */ |
| |
| len=strlen(line); |
| buff=str_alloc(len+2); /* +2 for CR+LF */ |
| strcpy(buff, line); |
| #ifdef USE_WIN32 |
| buff[len++]='\r'; /* CR */ |
| #endif /* USE_WIN32 */ |
| buff[len++]='\n'; /* LF */ |
| #ifdef USE_WIN32 |
| WriteFile(df->fh, buff, len, &num, NULL); |
| #else /* USE_WIN32 */ |
| /* no file -> write to stderr */ |
| num=write(df ? df->fd : 2, buff, len); |
| #endif /* USE_WIN32 */ |
| str_free(buff); |
| return (ssize_t)num; |
| } |
| |
| int file_permissions(const char *file_name) { |
| #if !defined(USE_WIN32) && !defined(USE_OS2) |
| struct stat sb; /* buffer for stat */ |
| |
| /* check permissions of the private key file */ |
| if(stat(file_name, &sb)) { |
| ioerror(file_name); |
| return 1; /* FAILED */ |
| } |
| if(sb.st_mode & 7) |
| s_log(LOG_WARNING, |
| "Insecure file permissions on %s", file_name); |
| #else |
| (void)file_name; /* skip warning about unused parameter */ |
| #endif |
| return 0; |
| } |
| |
| #ifdef USE_WIN32 |
| |
| LPTSTR str2tstr(LPCSTR in) { |
| LPTSTR out; |
| #ifdef UNICODE |
| int len; |
| |
| len=MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0); |
| if(!len) |
| return str_tprintf(TEXT("MultiByteToWideChar() failed")); |
| out=str_alloc(((size_t)len+1)*sizeof(WCHAR)); |
| len=MultiByteToWideChar(CP_UTF8, 0, in, -1, out, len); |
| if(!len) { |
| str_free(out); |
| return str_tprintf(TEXT("MultiByteToWideChar() failed")); |
| } |
| #else |
| /* FIXME: convert UTF-8 to native codepage */ |
| out=str_dup(in); |
| #endif |
| return out; |
| } |
| |
| LPSTR tstr2str(LPCTSTR in) { |
| LPSTR out; |
| #ifdef UNICODE |
| int len; |
| |
| len=WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); |
| if(!len) |
| return str_printf("WideCharToMultiByte() failed"); |
| out=str_alloc((size_t)len+1); |
| len=WideCharToMultiByte(CP_UTF8, 0, in, -1, out, len, NULL, NULL); |
| if(!len) { |
| str_free(out); |
| return str_printf("WideCharToMultiByte() failed"); |
| } |
| #else |
| /* FIXME: convert native codepage to UTF-8 */ |
| out=str_dup(in); |
| #endif |
| return out; |
| } |
| |
| #endif /* USE_WIN32 */ |
| |
| /* end of file.c */ |