ua_debug_dump_pkgs_file.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. /**
  5. * This code is used to generate a binary file for every request type
  6. * which can be sent from a client to the server.
  7. * These files form the basic corpus for fuzzing the server.
  8. */
  9. #ifndef UA_DEBUG_DUMP_PKGS_FILE
  10. #error UA_DEBUG_DUMP_PKGS_FILE must be defined
  11. #endif
  12. #include <open62541/transport_generated_encoding_binary.h>
  13. #include <open62541/types.h>
  14. #include <open62541/types_generated_encoding_binary.h>
  15. #include "server/ua_server_internal.h"
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <unistd.h>
  19. // This number is added to the end of every corpus data as 4 bytes.
  20. // It allows to generate valid corpus and then the fuzzer will use
  21. // these last 4 bytes to determine the simulated available RAM.
  22. // The fuzzer will then fiddle around with this number and (hopefully)
  23. // make it smaller, so that we can simulate Out-of-memory errors.
  24. #define UA_DUMP_RAM_SIZE 8 * 1024 * 1024
  25. unsigned int UA_dump_chunkCount = 0;
  26. char *UA_dump_messageTypes[] = {"ack", "hel", "msg", "opn", "clo", "err", "unk"};
  27. struct UA_dump_filename {
  28. const char *messageType;
  29. char serviceName[100];
  30. };
  31. void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection,
  32. UA_ByteString *messageBuffer);
  33. /**
  34. * Gets a pointer to the string representing the given message type from UA_dump_messageTypes.
  35. * Used for naming the dumped file.
  36. */
  37. static const char *
  38. UA_debug_dumpGetMessageTypePrefix(UA_UInt32 messageType) {
  39. switch(messageType & 0x00ffffff) {
  40. case UA_MESSAGETYPE_ACK:
  41. return UA_dump_messageTypes[0];
  42. case UA_MESSAGETYPE_HEL:
  43. return UA_dump_messageTypes[1];
  44. case UA_MESSAGETYPE_MSG:
  45. return UA_dump_messageTypes[2];
  46. case UA_MESSAGETYPE_OPN:
  47. return UA_dump_messageTypes[3];
  48. case UA_MESSAGETYPE_CLO:
  49. return UA_dump_messageTypes[4];
  50. case UA_MESSAGETYPE_ERR:
  51. return UA_dump_messageTypes[5];
  52. default:
  53. return UA_dump_messageTypes[6];
  54. }
  55. }
  56. /**
  57. * Decode the request message type from the given byte string and
  58. * set the global requestServiceName variable to the name of the request.
  59. * E.g. `GetEndpointsRequest`
  60. */
  61. static UA_StatusCode
  62. UA_debug_dumpSetServiceName(const UA_ByteString *msg, char serviceNameTarget[100]) {
  63. /* At 0, the nodeid starts... */
  64. size_t offset = 0;
  65. /* Decode the nodeid */
  66. UA_NodeId requestTypeId;
  67. UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
  68. if(retval != UA_STATUSCODE_GOOD)
  69. return retval;
  70. if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC || requestTypeId.namespaceIndex != 0) {
  71. snprintf(serviceNameTarget, 100, "invalid_request_id");
  72. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  73. }
  74. const UA_DataType *requestType = NULL;
  75. for (size_t i=0; i<UA_TYPES_COUNT; i++) {
  76. if (UA_TYPES[i].binaryEncodingId == requestTypeId.identifier.numeric) {
  77. requestType = &UA_TYPES[i];
  78. break;
  79. }
  80. }
  81. if (requestType == NULL) {
  82. snprintf(serviceNameTarget, 100, "invalid_request_no_type");
  83. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  84. }
  85. snprintf(serviceNameTarget, 100, "_%s", requestType->typeName);
  86. return UA_STATUSCODE_GOOD;
  87. }
  88. /**
  89. * We need to decode the given binary message to get the name of the called service.
  90. * This method is used if the connection has no channel yet.
  91. */
  92. static UA_StatusCode
  93. UA_debug_dump_setName_withoutChannel(UA_Server *server, UA_Connection *connection,
  94. UA_ByteString *message, struct UA_dump_filename* dump_filename) {
  95. size_t offset = 0;
  96. UA_TcpMessageHeader tcpMessageHeader;
  97. UA_StatusCode retval =
  98. UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
  99. if(retval != UA_STATUSCODE_GOOD)
  100. return retval;
  101. dump_filename->messageType =
  102. UA_debug_dumpGetMessageTypePrefix(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff);
  103. if ((tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) == UA_MESSAGETYPE_MSG) {
  104. // this should not happen in normal operation
  105. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Got MSG package without channel.");
  106. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  107. }
  108. return UA_STATUSCODE_GOOD;
  109. }
  110. /**
  111. * We need to decode the given binary message to get the name of the called service.
  112. * This method is used if the connection an established secure channel.
  113. *
  114. * message is the decoded message starting at the nodeid of the content type.
  115. */
  116. static void
  117. UA_debug_dump_setName_withChannel(void *application, UA_SecureChannel *channel,
  118. UA_MessageType messagetype, UA_UInt32 requestId,
  119. const UA_ByteString *message) {
  120. struct UA_dump_filename *dump_filename = (struct UA_dump_filename *)application;
  121. dump_filename->messageType = UA_debug_dumpGetMessageTypePrefix(messagetype);
  122. if(messagetype == UA_MESSAGETYPE_MSG)
  123. UA_debug_dumpSetServiceName(message, dump_filename->serviceName);
  124. }
  125. /**
  126. * Called in processCompleteChunk for every complete chunk which is received by the server.
  127. *
  128. * It will first try to decode the message to get the name of the called service.
  129. * When we have a name the message is dumped as binary to that file.
  130. * If the file already exists a new file will be created with a counter at the end.
  131. */
  132. void
  133. UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection,
  134. UA_ByteString *messageBuffer) {
  135. struct UA_dump_filename dump_filename;
  136. dump_filename.messageType = NULL;
  137. dump_filename.serviceName[0] = 0;
  138. if(!connection->channel) {
  139. UA_debug_dump_setName_withoutChannel(server, connection, messageBuffer, &dump_filename);
  140. } else {
  141. UA_SecureChannel dummy = *connection->channel;
  142. TAILQ_INIT(&dummy.messages);
  143. UA_ByteString messageBufferCopy;
  144. UA_ByteString_copy(messageBuffer, &messageBufferCopy);
  145. UA_SecureChannel_decryptAddChunk(&dummy, &messageBufferCopy, UA_TRUE);
  146. UA_SecureChannel_processCompleteMessages(&dummy, &dump_filename, UA_debug_dump_setName_withChannel);
  147. UA_SecureChannel_deleteMessages(&dummy);
  148. UA_ByteString_deleteMembers(&messageBufferCopy);
  149. }
  150. char fileName[250];
  151. snprintf(fileName, 255, "%s/%05d_%s%s", UA_CORPUS_OUTPUT_DIR, ++UA_dump_chunkCount,
  152. dump_filename.messageType ? dump_filename.messageType : "", dump_filename.serviceName);
  153. char dumpOutputFile[255];
  154. snprintf(dumpOutputFile, 255, "%s.bin", fileName);
  155. // check if file exists and if yes create a counting filename to avoid overwriting
  156. unsigned cnt = 1;
  157. while ( access( dumpOutputFile, F_OK ) != -1 ) {
  158. snprintf(dumpOutputFile, 255, "%s_%d.bin", fileName, cnt);
  159. cnt++;
  160. }
  161. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  162. "Dumping package %s", dumpOutputFile);
  163. FILE *write_ptr = fopen(dumpOutputFile, "ab");
  164. fwrite(messageBuffer->data, messageBuffer->length, 1, write_ptr); // write 10 bytes from our buffer
  165. // add the available memory size. See the UA_DUMP_RAM_SIZE define for more info.
  166. uint32_t ramSize = UA_DUMP_RAM_SIZE;
  167. fwrite(&ramSize, sizeof(ramSize), 1, write_ptr);
  168. fclose(write_ptr);
  169. }