Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nspr.h" |
michael@0 | 7 | |
michael@0 | 8 | #include <stdlib.h> |
michael@0 | 9 | #include <string.h> |
michael@0 | 10 | |
michael@0 | 11 | #include "plerror.h" |
michael@0 | 12 | #include "plgetopt.h" |
michael@0 | 13 | |
michael@0 | 14 | #define BASE_PORT 9867 |
michael@0 | 15 | #define DEFAULT_THREADS 1 |
michael@0 | 16 | #define DEFAULT_BACKLOG 10 |
michael@0 | 17 | #define DEFAULT_TIMEOUT 10 |
michael@0 | 18 | #define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */ |
michael@0 | 19 | |
michael@0 | 20 | typedef enum {running, stopped} Status; |
michael@0 | 21 | |
michael@0 | 22 | typedef struct Shared |
michael@0 | 23 | { |
michael@0 | 24 | PRLock *ml; |
michael@0 | 25 | PRCondVar *cv; |
michael@0 | 26 | PRBool passed; |
michael@0 | 27 | PRBool random; |
michael@0 | 28 | PRFileDesc *debug; |
michael@0 | 29 | PRIntervalTime timeout; |
michael@0 | 30 | PRFileDesc *listenSock; |
michael@0 | 31 | Status status; |
michael@0 | 32 | } Shared; |
michael@0 | 33 | |
michael@0 | 34 | static PRIntervalTime Timeout(const Shared *shared) |
michael@0 | 35 | { |
michael@0 | 36 | PRIntervalTime timeout = shared->timeout; |
michael@0 | 37 | if (shared->random) |
michael@0 | 38 | { |
michael@0 | 39 | PRIntervalTime half = timeout >> 1; /* one half of the interval */ |
michael@0 | 40 | PRIntervalTime quarter = half >> 1; /* one quarter of the interval */ |
michael@0 | 41 | /* something in [0..timeout / 2) */ |
michael@0 | 42 | PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE; |
michael@0 | 43 | timeout = (3 * quarter) + random; /* [75..125)% */ |
michael@0 | 44 | } |
michael@0 | 45 | return timeout; |
michael@0 | 46 | } /* Timeout */ |
michael@0 | 47 | |
michael@0 | 48 | static void Accept(void *arg) |
michael@0 | 49 | { |
michael@0 | 50 | PRStatus rv; |
michael@0 | 51 | char *buffer = NULL; |
michael@0 | 52 | PRNetAddr clientAddr; |
michael@0 | 53 | Shared *shared = (Shared*)arg; |
michael@0 | 54 | PRInt32 recv_length = 0, flags = 0; |
michael@0 | 55 | PRFileDesc *clientSock; |
michael@0 | 56 | PRIntn toread, byte, bytes, loop = 0; |
michael@0 | 57 | struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor; |
michael@0 | 58 | |
michael@0 | 59 | do |
michael@0 | 60 | { |
michael@0 | 61 | PRUint32 checksum = 0; |
michael@0 | 62 | if (NULL != shared->debug) |
michael@0 | 63 | PR_fprintf(shared->debug, "[%d]accepting ... ", loop++); |
michael@0 | 64 | clientSock = PR_Accept( |
michael@0 | 65 | shared->listenSock, &clientAddr, Timeout(shared)); |
michael@0 | 66 | if (clientSock != NULL) |
michael@0 | 67 | { |
michael@0 | 68 | if (NULL != shared->debug) |
michael@0 | 69 | PR_fprintf(shared->debug, "reading length ... "); |
michael@0 | 70 | bytes = PR_Recv( |
michael@0 | 71 | clientSock, &descriptor, sizeof(descriptor), |
michael@0 | 72 | flags, Timeout(shared)); |
michael@0 | 73 | if (sizeof(descriptor) == bytes) |
michael@0 | 74 | { |
michael@0 | 75 | /* and, before doing something stupid ... */ |
michael@0 | 76 | descriptor.length = PR_ntohl(descriptor.length); |
michael@0 | 77 | descriptor.checksum = PR_ntohl(descriptor.checksum); |
michael@0 | 78 | if (NULL != shared->debug) |
michael@0 | 79 | PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length); |
michael@0 | 80 | toread = descriptor.length; |
michael@0 | 81 | if (recv_length < descriptor.length) |
michael@0 | 82 | { |
michael@0 | 83 | if (NULL != buffer) PR_DELETE(buffer); |
michael@0 | 84 | buffer = (char*)PR_MALLOC(descriptor.length); |
michael@0 | 85 | recv_length = descriptor.length; |
michael@0 | 86 | } |
michael@0 | 87 | for (toread = descriptor.length; toread > 0; toread -= bytes) |
michael@0 | 88 | { |
michael@0 | 89 | bytes = PR_Recv( |
michael@0 | 90 | clientSock, &buffer[descriptor.length - toread], |
michael@0 | 91 | toread, flags, Timeout(shared)); |
michael@0 | 92 | if (-1 == bytes) |
michael@0 | 93 | { |
michael@0 | 94 | if (NULL != shared->debug) |
michael@0 | 95 | PR_fprintf(shared->debug, "read data failed..."); |
michael@0 | 96 | bytes = 0; |
michael@0 | 97 | } |
michael@0 | 98 | } |
michael@0 | 99 | } |
michael@0 | 100 | else if (NULL != shared->debug) |
michael@0 | 101 | { |
michael@0 | 102 | PR_fprintf(shared->debug, "read desciptor failed..."); |
michael@0 | 103 | descriptor.length = -1; |
michael@0 | 104 | } |
michael@0 | 105 | if (NULL != shared->debug) |
michael@0 | 106 | PR_fprintf(shared->debug, "closing"); |
michael@0 | 107 | rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); |
michael@0 | 108 | if ((PR_FAILURE == rv) && (NULL != shared->debug)) |
michael@0 | 109 | { |
michael@0 | 110 | PR_fprintf(shared->debug, " failed"); |
michael@0 | 111 | shared->passed = PR_FALSE; |
michael@0 | 112 | } |
michael@0 | 113 | rv = PR_Close(clientSock); |
michael@0 | 114 | if (PR_FAILURE == rv) if (NULL != shared->debug) |
michael@0 | 115 | { |
michael@0 | 116 | PR_fprintf(shared->debug, " failed"); |
michael@0 | 117 | shared->passed = PR_FALSE; |
michael@0 | 118 | } |
michael@0 | 119 | if (descriptor.length > 0) |
michael@0 | 120 | { |
michael@0 | 121 | for (byte = 0; byte < descriptor.length; ++byte) |
michael@0 | 122 | { |
michael@0 | 123 | PRUint32 overflow = checksum & 0x80000000; |
michael@0 | 124 | checksum = (checksum << 1); |
michael@0 | 125 | if (0x00000000 != overflow) checksum += 1; |
michael@0 | 126 | checksum += buffer[byte]; |
michael@0 | 127 | } |
michael@0 | 128 | if ((descriptor.checksum != checksum) && (NULL != shared->debug)) |
michael@0 | 129 | { |
michael@0 | 130 | PR_fprintf(shared->debug, " ... data mismatch"); |
michael@0 | 131 | shared->passed = PR_FALSE; |
michael@0 | 132 | } |
michael@0 | 133 | } |
michael@0 | 134 | else if (0 == descriptor.length) |
michael@0 | 135 | { |
michael@0 | 136 | PR_Lock(shared->ml); |
michael@0 | 137 | shared->status = stopped; |
michael@0 | 138 | PR_NotifyCondVar(shared->cv); |
michael@0 | 139 | PR_Unlock(shared->ml); |
michael@0 | 140 | } |
michael@0 | 141 | if (NULL != shared->debug) |
michael@0 | 142 | PR_fprintf(shared->debug, "\n"); |
michael@0 | 143 | } |
michael@0 | 144 | else |
michael@0 | 145 | { |
michael@0 | 146 | if (PR_PENDING_INTERRUPT_ERROR != PR_GetError()) |
michael@0 | 147 | { |
michael@0 | 148 | if (NULL != shared->debug) PL_PrintError("Accept"); |
michael@0 | 149 | shared->passed = PR_FALSE; |
michael@0 | 150 | } |
michael@0 | 151 | } |
michael@0 | 152 | } while (running == shared->status); |
michael@0 | 153 | if (NULL != buffer) PR_DELETE(buffer); |
michael@0 | 154 | } /* Accept */ |
michael@0 | 155 | |
michael@0 | 156 | PRIntn Tmoacc(PRIntn argc, char **argv) |
michael@0 | 157 | { |
michael@0 | 158 | PRStatus rv; |
michael@0 | 159 | PRIntn exitStatus; |
michael@0 | 160 | PRIntn index; |
michael@0 | 161 | Shared *shared; |
michael@0 | 162 | PLOptStatus os; |
michael@0 | 163 | PRThread **thread; |
michael@0 | 164 | PRNetAddr listenAddr; |
michael@0 | 165 | PRSocketOptionData sockOpt; |
michael@0 | 166 | PRIntn timeout = DEFAULT_TIMEOUT; |
michael@0 | 167 | PRIntn threads = DEFAULT_THREADS; |
michael@0 | 168 | PRIntn backlog = DEFAULT_BACKLOG; |
michael@0 | 169 | PRThreadScope thread_scope = PR_LOCAL_THREAD; |
michael@0 | 170 | |
michael@0 | 171 | PLOptState *opt = PL_CreateOptState(argc, argv, "dGb:t:T:R"); |
michael@0 | 172 | |
michael@0 | 173 | shared = PR_NEWZAP(Shared); |
michael@0 | 174 | |
michael@0 | 175 | shared->debug = NULL; |
michael@0 | 176 | shared->passed = PR_TRUE; |
michael@0 | 177 | shared->random = PR_TRUE; |
michael@0 | 178 | shared->status = running; |
michael@0 | 179 | shared->ml = PR_NewLock(); |
michael@0 | 180 | shared->cv = PR_NewCondVar(shared->ml); |
michael@0 | 181 | |
michael@0 | 182 | while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) |
michael@0 | 183 | { |
michael@0 | 184 | if (PL_OPT_BAD == os) continue; |
michael@0 | 185 | switch (opt->option) |
michael@0 | 186 | { |
michael@0 | 187 | case 'd': /* debug mode */ |
michael@0 | 188 | shared->debug = PR_GetSpecialFD(PR_StandardError); |
michael@0 | 189 | break; |
michael@0 | 190 | case 'G': /* use global threads */ |
michael@0 | 191 | thread_scope = PR_GLOBAL_THREAD; |
michael@0 | 192 | break; |
michael@0 | 193 | case 'b': /* size of listen backlog */ |
michael@0 | 194 | backlog = atoi(opt->value); |
michael@0 | 195 | break; |
michael@0 | 196 | case 't': /* number of threads doing accept */ |
michael@0 | 197 | threads = atoi(opt->value); |
michael@0 | 198 | break; |
michael@0 | 199 | case 'T': /* timeout used for network operations */ |
michael@0 | 200 | timeout = atoi(opt->value); |
michael@0 | 201 | break; |
michael@0 | 202 | case 'R': /* randomize the timeout values */ |
michael@0 | 203 | shared->random = PR_TRUE; |
michael@0 | 204 | break; |
michael@0 | 205 | default: |
michael@0 | 206 | break; |
michael@0 | 207 | } |
michael@0 | 208 | } |
michael@0 | 209 | PL_DestroyOptState(opt); |
michael@0 | 210 | if (0 == threads) threads = DEFAULT_THREADS; |
michael@0 | 211 | if (0 == backlog) backlog = DEFAULT_BACKLOG; |
michael@0 | 212 | if (0 == timeout) timeout = DEFAULT_TIMEOUT; |
michael@0 | 213 | |
michael@0 | 214 | PR_STDIO_INIT(); |
michael@0 | 215 | memset(&listenAddr, 0, sizeof(listenAddr)); |
michael@0 | 216 | rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr); |
michael@0 | 217 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 218 | |
michael@0 | 219 | shared->timeout = PR_SecondsToInterval(timeout); |
michael@0 | 220 | |
michael@0 | 221 | /* First bind to the socket */ |
michael@0 | 222 | shared->listenSock = PR_NewTCPSocket(); |
michael@0 | 223 | if (shared->listenSock) |
michael@0 | 224 | { |
michael@0 | 225 | sockOpt.option = PR_SockOpt_Reuseaddr; |
michael@0 | 226 | sockOpt.value.reuse_addr = PR_TRUE; |
michael@0 | 227 | rv = PR_SetSocketOption(shared->listenSock, &sockOpt); |
michael@0 | 228 | PR_ASSERT(PR_SUCCESS == rv); |
michael@0 | 229 | rv = PR_Bind(shared->listenSock, &listenAddr); |
michael@0 | 230 | if (rv != PR_FAILURE) |
michael@0 | 231 | { |
michael@0 | 232 | rv = PR_Listen(shared->listenSock, threads + backlog); |
michael@0 | 233 | if (PR_SUCCESS == rv) |
michael@0 | 234 | { |
michael@0 | 235 | thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); |
michael@0 | 236 | for (index = 0; index < threads; ++index) |
michael@0 | 237 | { |
michael@0 | 238 | thread[index] = PR_CreateThread( |
michael@0 | 239 | PR_USER_THREAD, Accept, shared, |
michael@0 | 240 | PR_PRIORITY_NORMAL, thread_scope, |
michael@0 | 241 | PR_JOINABLE_THREAD, 0); |
michael@0 | 242 | PR_ASSERT(NULL != thread[index]); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | PR_Lock(shared->ml); |
michael@0 | 246 | while (shared->status == running) |
michael@0 | 247 | PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 248 | PR_Unlock(shared->ml); |
michael@0 | 249 | for (index = 0; index < threads; ++index) |
michael@0 | 250 | { |
michael@0 | 251 | rv = PR_Interrupt(thread[index]); |
michael@0 | 252 | PR_ASSERT(PR_SUCCESS== rv); |
michael@0 | 253 | rv = PR_JoinThread(thread[index]); |
michael@0 | 254 | PR_ASSERT(PR_SUCCESS== rv); |
michael@0 | 255 | } |
michael@0 | 256 | PR_DELETE(thread); |
michael@0 | 257 | } |
michael@0 | 258 | else |
michael@0 | 259 | { |
michael@0 | 260 | if (shared->debug) PL_PrintError("Listen"); |
michael@0 | 261 | shared->passed = PR_FALSE; |
michael@0 | 262 | } |
michael@0 | 263 | } |
michael@0 | 264 | else |
michael@0 | 265 | { |
michael@0 | 266 | if (shared->debug) PL_PrintError("Bind"); |
michael@0 | 267 | shared->passed = PR_FALSE; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | PR_Close(shared->listenSock); |
michael@0 | 271 | } |
michael@0 | 272 | else |
michael@0 | 273 | { |
michael@0 | 274 | if (shared->debug) PL_PrintError("Create"); |
michael@0 | 275 | shared->passed = PR_FALSE; |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | PR_DestroyCondVar(shared->cv); |
michael@0 | 279 | PR_DestroyLock(shared->ml); |
michael@0 | 280 | |
michael@0 | 281 | PR_fprintf( |
michael@0 | 282 | PR_GetSpecialFD(PR_StandardError), "%s\n", |
michael@0 | 283 | ((shared->passed) ? "PASSED" : "FAILED")); |
michael@0 | 284 | |
michael@0 | 285 | exitStatus = (shared->passed) ? 0 : 1; |
michael@0 | 286 | PR_DELETE(shared); |
michael@0 | 287 | return exitStatus; |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | int main(int argc, char **argv) |
michael@0 | 291 | { |
michael@0 | 292 | return (PR_VersionCheck(PR_VERSION)) ? |
michael@0 | 293 | PR_Initialize(Tmoacc, argc, argv, 4) : -1; |
michael@0 | 294 | } /* main */ |
michael@0 | 295 | |
michael@0 | 296 | /* tmoacc */ |