1 概述

在 Wireshark 中, 通过 文件 菜单中的 导出对象 子菜单, 可以导出 HTTP, TFTP 等协议传输的数据.
企业微信截图_16358321013797.png
比如选中 HTTP, 弹出对话框中可看到可导出的文件, 可以预览也可以另存为文件.
企业微信截图_16358322161378.png

TShark 也支持这个功能, 只要使用 --export-objects 选项就好:

  1. --export-objects <protocol>,<destdir>
  2. Export all objects within a protocol into directory destdir. The available values for protocol can be listed with --export-objects help.
  3. The objects are directly saved in the given directory. Filenames are dependent on the dissector, but typically it is named after the basename of a file. Duplicate files are not overwritten, instead an increasing number is appended before the file extension.
  4. This interface is subject to change, adding the possibility to filter on files.

TShark 执行示例:

  1. zzq@vbox:~/dev/wireshark_build/run
  2. $mkdir extmp
  3. zzq@vbox:~/dev/wireshark_build/run
  4. $./tshark --export-objects http,extmp -r ~/pcap/http_gnu.pcap
  5. 1 0.000000 192.168.1.103 209.51.188.148 TCP 66 6507 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
  6. 2 0.309763 209.51.188.148 192.168.1.103 TCP 66 80 6507 [SYN, ACK] Seq=0 Ack=1 Win=29200 Len=0 MSS=1440 SACK_PERM=1 WS=128
  7. 3 0.309827 192.168.1.103 209.51.188.148 TCP 54 6507 80 [ACK] Seq=1 Ack=1 Win=132352 Len=0
  8. 4 0.310076 192.168.1.103 209.51.188.148 HTTP 466 GET / HTTP/1.1
  9. ...
  10. 45 1.922438 192.168.1.103 209.51.188.148 HTTP 379 GET /print.min.css HTTP/1.1
  11. 46 2.232797 209.51.188.148 192.168.1.103 HTTP 1414 HTTP/1.1 200 OK (text/css)
  12. 47 2.274466 192.168.1.103 209.51.188.148 TCP 54 6507 80 [ACK] Seq=1804 Ack=30667 Win=132352 Len=0
  13. 48 5.235437 209.51.188.148 192.168.1.103 TCP 60 80 6507 [FIN, ACK] Seq=30667 Ack=1804 Win=34560 Len=0
  14. 49 5.235469 192.168.1.103 209.51.188.148 TCP 54 6507 80 [ACK] Seq=1804 Ack=30668 Win=132352 Len=0
  15. zzq@vbox:~/dev/wireshark_build/run
  16. $ls extmp/
  17. %2f heckert_gnu.transp.small.png hyperbola-i3-thumb.jpg print.min.css

本文通过跟踪分析 TShark 来探索导出对象的原理. 调试示例:

  1. zzq@vbox:~/dev/wireshark_build/run
  2. $gdb ./tshark
  3. ...
  4. Reading symbols from ./tshark...
  5. (gdb) b eo_draw
  6. Breakpoint 1 at 0x267db: file /home/zzq/dev/wireshark/ui/cli/tap-exportobject.c, line 103.
  7. (gdb) r --export-objects http,extmp -r ~/pcap/http_gnu.pcap
  8. Starting program: /home/zzq/dev/wireshark_build/run/tshark --export-objects http,extmp -r ~/pcap/http_gnu.pcap
  9. [Thread debugging using libthread_db enabled]
  10. Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
  11. [New Thread 0x7fffee845700 (LWP 9624)]
  12. [Thread 0x7fffee845700 (LWP 9624) exited]
  13. [New Thread 0x7fffee845700 (LWP 9625)]
  14. [Thread 0x7fffee845700 (LWP 9625) exited]
  15. 1 0.000000 192.168.1.103 209.51.188.148 TCP 66 6507 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
  16. ...
  17. 49 5.235469 192.168.1.103 209.51.188.148 TCP 54 6507 80 [ACK] Seq=1804 Ack=30668 Win=132352 Len=0
  18. Thread 1 "tshark" hit Breakpoint 1, eo_draw (tapdata=0x55555592be40) at /home/zzq/dev/wireshark/ui/cli/tap-exportobject.c:103
  19. 103 {
  20. (gdb) bt
  21. #0 eo_draw (tapdata=0x55555592be40) at /home/zzq/dev/wireshark/ui/cli/tap-exportobject.c:103
  22. #1 0x00007ffff3fc9754 in draw_tap_listeners (draw_all=1) at /home/zzq/dev/wireshark/epan/tap.c:442
  23. #2 0x0000555555574194 in main (argc=5, argv=0x7fffffffe268) at /home/zzq/dev/wireshark/tshark.c:2310

以下简称导出对象为 eo. Wireshark 导出对象功能是基于 tap 实现的, 理解其原理需要先理解 Wireshark tap 机制.

2 数据结构

register_eo

eo 注册表项. 需要支持对象导出的协议应在初始化(如epan_init流程中)时进行 eo 事项注册, 如处理函数等. 此注册会生成 eo 注册表项.

  1. // epan/export_object.c
  2. struct register_eo {
  3. int proto_id; /* protocol id (0-indexed) */
  4. const char* tap_listen_str; /* string used in register_tap_listener (NULL to use protocol name) */
  5. tap_packet_cb eo_func; /* function to be called for new incoming packets for SRT */
  6. export_object_gui_reset_cb reset_cb; /* function to parse parameters of optional arguments of tap string */
  7. };
  8. // epan/export_object.h
  9. /** Structure for information about a registered exported object */
  10. typedef struct register_eo register_eo_t;

其中,

  • proto_id: 协议 id
  • taplisten_str: eo tap listener 名称, 比如 HTTP 协议对应的是 _http_eo
  • eo_func: eo tap 处理函数, 原型是

    1. typedef tap_packet_status (*tap_packet_cb)(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *data);

    此函数非常关键, 会由 tap 机制在合适的时机调用, 用于将协议数据提取出来, 暂存在 eo 数据结构中. 不同协议的 eo_func 一般不同, 如 HTTP 协议为 http_eo_packet.

    registered_eo_tables

    全局 eo 注册表, 以红黑树实现, 查找 key 为协议过滤名字符串, 如 http. 注意不是 http_eo, 那是 tap listener 的名字字符串.

    1. // epan/export_object.c
    2. static wmem_tree_t *registered_eo_tables = NULL;

    [register_export_object](#L14OR) 函数会根据参数生成新的 eo 注册表项, 并将它添加到全局注册表 registered_eo_tables 中.

    eo_opts

    eo 全局选项, 以哈希表实现, key 为协议名, value 为保存目录.

    1. // ui/cli/tap-exportobject.c
    2. static GHashTable* eo_opts = NULL;

    export_object_entry_t

    表示协议实际解析出的数据, 如 HTTP 中传输的文件等.

    1. // epan/export_object.h
    2. typedef struct _export_object_entry_t {
    3. guint32 pkt_num;
    4. gchar *hostname;
    5. gchar *content_type;
    6. gchar *filename;
    7. /* We need to store a 64 bit integer to hold a file length
    8. (was guint payload_len;)
    9. XXX - we store the entire object in the program's address space,
    10. so the *real* maximum object size is size_t; if we were to export
    11. objects by going through all of the packets containing data from
    12. the object, one packet at a time, and write the object incrementally,
    13. we could support objects that don't fit into the address space. */
    14. gint64 payload_len;
    15. guint8 *payload_data;
    16. } export_object_entry_t;

    export_object_list_t

    tap listener 结构体中包括类型为 void* 的 tap 上下文数据, 不同类型 tap 可能是不同的, 对于 eo, 它的实际类型为 export_object_list_t:

    1. // epan/export_object.h
    2. typedef struct _export_object_list_t {
    3. export_object_object_list_add_entry_cb add_entry; //GUI specific handler for adding an object entry
    4. export_object_object_list_get_entry_cb get_entry; //GUI specific handler for retrieving an object entry
    5. void* gui_data; //GUI specific data (for UI representation)
    6. } export_object_list_t;

    在报文处理过程中, tap 机制会调用 tap_packet_cb 类型的回调函数, 它的第一个实参就是这里所说的”tap 上下文数据”, 对于 eo 则就是 export_object_list_t 指针了. 联系上下文, 也就是 register_eo 结构体中的 eo_func 成员, 具体到 HTTP 协议就是 http_eo_packet 函数.

此结构体中,

  • addentry: 把导出数据添加到链表, 这链表就是下文的”导出数据表” export_object_list_gui_t 中的链表. add_entry 的实际函数是 _ui/cli/tap-exportobject.c 中的 object_list_add_entry
  • getentry: 把链表中的导出数据取回来. get_entry 的实际函数是 _ui/cli/tap-exportobject.c 中的 object_list_get_entry
  • gui_data: export_object_list_gui_t 指针

    export_object_list_gui_t

    导出数据会添加到导出数据表中.

    1. // ui/cli/tap-exportobject.c
    2. typedef struct _export_object_list_gui_t {
    3. GSList *entries;
    4. register_eo_t* eo;
    5. } export_object_list_gui_t;

    其中,

  • entries: 同一协议的多个导出数据会挂到 entries 列表中

  • eo: 初始化注册的 eo 注册表项

    数据结构关系

    eo_datastructure.png

    3 流程

    eo 流程主要分 3 个阶段:
  1. 注册: 协议需要注册自己的 eo 处理函数, 而主程序(如 TShark)需要注册 tap listener
  2. 数据提取: 协议在解析过程中提出数据, 并暂存到内部数据结构中
  3. 数据导出: 将提取好的数据写入文件等

整体流程见下文中的图, 结合图看可以更容易理解.

注册

register_export_object

协议应在初始化时调用此函数进行 eo 注册, 其中的 export_packet_func 非常关键, 是协议特定的数据导出回调函数. 此函数会新建 eo 注册表项, 并添加到全局表中, 并注册 tap.

  1. // epan/export_object.c
  2. int
  3. register_export_object(const int proto_id, tap_packet_cb export_packet_func, export_object_gui_reset_cb reset_cb)
  4. {
  5. register_eo_t *table;
  6. DISSECTOR_ASSERT(export_packet_func);
  7. table = wmem_new(wmem_epan_scope(), register_eo_t);
  8. table->proto_id = proto_id;
  9. table->tap_listen_str = wmem_strdup_printf(wmem_epan_scope(), "%s_eo", proto_get_protocol_filter_name(proto_id));
  10. table->eo_func = export_packet_func;
  11. table->reset_cb = reset_cb;
  12. if (registered_eo_tables == NULL)
  13. registered_eo_tables = wmem_tree_new(wmem_epan_scope());
  14. wmem_tree_insert_string(registered_eo_tables, proto_get_protocol_filter_name(proto_id), table, 0);
  15. return register_tap(table->tap_listen_str);
  16. }

此函数的返回值是 tap 句柄, 之后实际提取协议数据时要用到.
在 HTTP 中的注册示例:

  1. // epan/dissectors/packet-http.c
  2. void
  3. proto_register_http(void)
  4. {
  5. ...
  6. /*
  7. * Register for tapping
  8. */
  9. http_tap = register_tap("http"); /* HTTP statistics tap */
  10. http_follow_tap = register_tap("http_follow"); /* HTTP Follow tap */
  11. ...
  12. register_follow_stream(proto_http, "http_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter,
  13. tcp_port_to_display, follow_tvb_tap_listener);
  14. http_eo_tap = register_export_object(proto_http, http_eo_packet, NULL);
  15. }

其中 http_eo_packet 就是 HTTP 协议对就的 eo 回调函数, 其具体实现见下文.

eo_tap_opt_add

向 eo 传递命令行选项, 语法是 <protocol>,<destdir>. eo 选项会被添加到全局的 eo_opts.

  1. // ui/cli/tap-exportobject.h
  2. /* will be called by main each time a --export-objects option is found */
  3. gboolean eo_tap_opt_add(const char *optarg);
  4. // ui/cli/tap-exportobject.c
  5. gboolean eo_tap_opt_add(const char *option_string)
  6. {
  7. gchar** splitted;
  8. if (!eo_opts)
  9. eo_opts = g_hash_table_new(g_str_hash,g_str_equal);
  10. splitted = g_strsplit(option_string, ",", 2);
  11. if ((splitted[0] == NULL) || (splitted[1] == NULL) || (get_eo_by_name(splitted[0]) == NULL))
  12. {
  13. fprintf(stderr, "tshark: \"--export-objects\" are specified as: <protocol>,<destdir>\n");
  14. fprintf(stderr, "tshark: The available export object types for the \"--export-objects\" option are:\n");
  15. eo_list_object_types();
  16. }
  17. else
  18. {
  19. gchar* dir = (gchar*)g_hash_table_lookup(eo_opts, splitted[0]);
  20. /* Since we're saving all objects from a protocol,
  21. it can only be listed once */
  22. if (dir == NULL) {
  23. g_hash_table_insert(eo_opts, splitted[0], splitted[1]);
  24. g_free(splitted);
  25. return TRUE;
  26. }
  27. else
  28. ...
  29. }
  30. g_strfreev(splitted);
  31. return FALSE;
  32. }

此函数在 TShark 的 main 函数中执行, 具体是在命令行解析的时候, 如果用户指定了 --export-objects 选项就执行:

  1. // tshark.c
  2. int
  3. main(int argc, char *argv[])
  4. {
  5. ...
  6. case LONGOPT_EXPORT_OBJECTS: /* --export-objects */
  7. if (strcmp("help", optarg) == 0) {
  8. fprintf(stderr, "tshark: The available export object types for the \"--export-objects\" option are:\n");
  9. eo_list_object_types();
  10. exit_status = EXIT_SUCCESS;
  11. goto clean_exit;
  12. }
  13. if (!eo_tap_opt_add(optarg)) {
  14. exit_status = INVALID_OPTION;
  15. goto clean_exit;
  16. }
  17. ...
  18. }

start_exportobjects

声明开启 eo 操作. 这会对全局 eo 选项(一个哈希表)中的每一项调用 exportobject_handler 函数.

  1. // ui/cli/tap-exportobject.c
  2. static void
  3. exportobject_handler(gpointer key, gpointer value _U_, gpointer user_data _U_)
  4. {
  5. GString *error_msg;
  6. export_object_list_t *tap_data;
  7. export_object_list_gui_t *object_list;
  8. register_eo_t* eo;
  9. eo = get_eo_by_name((const char*)key);
  10. ...
  11. tap_data = g_new0(export_object_list_t,1);
  12. object_list = g_new0(export_object_list_gui_t,1);
  13. tap_data->add_entry = object_list_add_entry;
  14. tap_data->get_entry = object_list_get_entry;
  15. tap_data->gui_data = (void*)object_list;
  16. object_list->eo = eo;
  17. /* Data will be gathered via a tap callback */
  18. error_msg = register_tap_listener(get_eo_tap_listener_name(eo), tap_data, NULL, 0,
  19. NULL, get_eo_packet_func(eo), eo_draw, NULL);
  20. if (error_msg) {
  21. ...
  22. }
  23. }
  24. void start_exportobjects(void)
  25. {
  26. if (eo_opts != NULL)
  27. g_hash_table_foreach(eo_opts, exportobject_handler, NULL);
  28. }

调用这个函数会调用 register_tap_listener, 使得 eo 与 tap 机制彻底绑定起来. 此函数在 TShark 的 main 函数中执行:

  1. // tshark.c
  2. int
  3. main(int argc, char *argv[])
  4. {
  5. ...
  6. prefs_apply_all();
  7. /* We can also enable specified taps for export object */
  8. start_exportobjects();
  9. ...
  10. }

提取数据

在报文的处理过程中, 注册时构建好的 eo - tap 联动机制将发挥作用, 完成协议数据的提取. 具体来说, 协议解析器与 tap 机制要完成各自任务:

  • 协议解析器: 解析报文, 在准备好协议数据时, 根据这些数据构造 eo 信息, 然后调用 tap_queue_packet, 把 eo 信息加入 tap 队列.
  • tap 机制: 在实际解析报文前初始化 tap 队列, 而在之后遍历 tap 队列完成处理. 在处理 tap 队列的过程中, 会调用到协议注册的 eo 处理函数.

协议解析器的处理示例如 HTTP:

  1. // epan/dissectors/packet-http.c
  2. static int
  3. dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
  4. proto_tree *tree, http_conv_t *conv_data,
  5. const char* proto_tag, int proto, gboolean end_of_stream)
  6. {
  7. ...
  8. if (datalen > 0) {
  9. ...
  10. /* Save values for the Export Object GUI feature if we have
  11. * an active listener to process it (which happens when
  12. * the export object window is open). */
  13. if(have_tap_listener(http_eo_tap)) {
  14. eo_info = wmem_new(wmem_packet_scope(), http_eo_t);
  15. eo_info->hostname = conv_data->http_host;
  16. eo_info->filename = conv_data->request_uri;
  17. eo_info->content_type = headers.content_type;
  18. eo_info->payload_len = tvb_captured_length(next_tvb);
  19. eo_info->payload_data = tvb_get_ptr(next_tvb, 0, eo_info->payload_len);
  20. tap_queue_packet(http_eo_tap, pinfo, eo_info);
  21. }
  22. ...
  23. }
  24. ...
  25. }

此时已经把 HTTP 报文解析完毕, 于是根据解析出的数据构造 http_eo_t, 并将其加入 tap 队列.

tap 机制包含在协议解析流程中, 如 epan_dissect_run_with_taps:

  1. // epan/epan.c
  2. void
  3. epan_dissect_run_with_taps(epan_dissect_t *edt, int file_type_subtype,
  4. wtap_rec *rec, tvbuff_t *tvb, frame_data *fd,
  5. column_info *cinfo)
  6. {
  7. wmem_enter_packet_scope();
  8. tap_queue_init(edt);
  9. dissect_record(edt, file_type_subtype, rec, tvb, fd, cinfo);
  10. tap_push_tapped_queue(edt);
  11. /* free all memory allocated */
  12. wmem_leave_packet_scope();
  13. }

其中, dissect_record 中调用到上文的 dissect_http_message, 它在必要时填充 tap 队列; 而 tap_push_tapped_queue 会处理现有的 tap 队列, 对队中每一项调用处理函数(tl->packet()):

  1. // epan/tap.c
  2. /* this function is called after a packet has been fully dissected to push the tapped
  3. data to all extensions that has callbacks registered.
  4. */
  5. void
  6. tap_push_tapped_queue(epan_dissect_t *edt)
  7. {
  8. tap_packet_t *tp;
  9. tap_listener_t *tl;
  10. guint i;
  11. /* nothing to do, just return */
  12. if(!tapping_is_active){
  13. return;
  14. }
  15. tapping_is_active=FALSE;
  16. /* nothing to do, just return */
  17. if(!tap_packet_index){
  18. return;
  19. }
  20. /* loop over all tap listeners and call the listener callback
  21. for all packets that match the filter. */
  22. for(i=0;i<tap_packet_index;i++){
  23. for(tl=tap_listener_queue;tl;tl=tl->next){
  24. tp=&tap_packet_array[i];
  25. /* Don't tap the packet if it's an "error packet"
  26. * unless the listener has requested that we do so.
  27. */
  28. if (!(tp->flags & TAP_PACKET_IS_ERROR_PACKET) || (tl->flags & TL_REQUIRES_ERROR_PACKETS))
  29. {
  30. if(tp->tap_id==tl->tap_id){
  31. if(!tl->packet){
  32. /* There isn't a per-packet
  33. * routine for this tap.
  34. */
  35. continue;
  36. }
  37. ...
  38. /* So call the per-packet routine. */
  39. tap_packet_status status;
  40. status = tl->packet(tl->tapdata, tp->pinfo, edt, tp->tap_specific_data);
  41. ...
  42. }
  43. }
  44. }
  45. }
  46. }

这里 tap listener 的报文处理函数 packet, 就是在上文的 start_exportobjects 中配置好的, 因为其中调用 register_tap_listener 注册了回调函数.
对于 HTTP, 这个函数就是 http_eo_packet:

  1. // epan/dissectors/packet-http.c
  2. static tap_packet_status
  3. http_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data)
  4. {
  5. export_object_list_t *object_list = (export_object_list_t *)tapdata;
  6. const http_eo_t *eo_info = (const http_eo_t *)data;
  7. export_object_entry_t *entry;
  8. if(eo_info) { /* We have data waiting for us */
  9. /* These values will be freed when the Export Object window
  10. * is closed. */
  11. entry = g_new(export_object_entry_t, 1);
  12. entry->pkt_num = pinfo->num;
  13. entry->hostname = g_strdup(eo_info->hostname);
  14. entry->content_type = g_strdup(eo_info->content_type);
  15. entry->filename = eo_info->filename ? g_path_get_basename(eo_info->filename) : NULL;
  16. entry->payload_len = eo_info->payload_len;
  17. entry->payload_data = (guint8 *)g_memdup(eo_info->payload_data, eo_info->payload_len);
  18. object_list->add_entry(object_list->gui_data, entry);
  19. return TAP_PACKET_REDRAW; /* State changed - window should be redrawn */
  20. } else {
  21. return TAP_PACKET_DONT_REDRAW; /* State unchanged - no window updates needed */
  22. }
  23. }

此函数根据报文解析时得到的 HTTP 导出对象信息构造导出数据对象(entry), 并把它挂到全局链表上, 可参考上文的数据结构关系图来帮助理解.
HTTP 导出对象信息:

  1. // epan/dissectors/packet-http.c
  2. /* Used for HTTP Export Object feature */
  3. typedef struct _http_eo_t {
  4. guint32 pkt_num;
  5. gchar *hostname;
  6. gchar *filename;
  7. gchar *content_type;
  8. guint32 payload_len;
  9. const guint8 *payload_data;
  10. } http_eo_t;

可见包括解析出的 Host, Content-Type 字符串, 以及对应的数据与长度.

导出数据

整个 pcap 文件解析完成后(本文不涉及网卡抓包场景), TShark 会调用 draw_tap_listeners, 最终导致之前提取的数据写入文件.

  1. if (draw_taps)
  2. draw_tap_listeners(TRUE);

此函数遍历所有 tap listeners, 调用其绑定的 draw 回调函数:

  1. // epan/tap.c
  2. /* This function is called when we need to redraw all tap listeners, for example
  3. when we open/start a new capture or if we need to rescan the packet list.
  4. It should be called from a low priority thread say once every 3 seconds
  5. If draw_all is true, redraw all applications regardless if they have
  6. changed or not.
  7. */
  8. void
  9. draw_tap_listeners(gboolean draw_all)
  10. {
  11. tap_listener_t *tl;
  12. for(tl=tap_listener_queue;tl;tl=tl->next){
  13. if(tl->needs_redraw || draw_all){
  14. if(tl->draw){
  15. tl->draw(tl->tapdata);
  16. }
  17. }
  18. tl->needs_redraw=FALSE;
  19. }
  20. }

对于 eo tap, 这个 draw 函数就是 eo_draw, 它是在 start_exportobjects 时配置好的:

  1. // ui/cli/tap-exportobject.c
  2. /* This is just for writing Exported Objects to a file */
  3. static void
  4. eo_draw(void *tapdata)
  5. {
  6. export_object_list_t *tap_object = (export_object_list_t *)tapdata;
  7. export_object_list_gui_t *object_list = (export_object_list_gui_t*)tap_object->gui_data;
  8. GSList *slist = object_list->entries;
  9. export_object_entry_t *entry;
  10. gchar* save_in_path = (gchar*)g_hash_table_lookup(eo_opts, proto_get_protocol_filter_name(get_eo_proto_id(object_list->eo)));
  11. GString *safe_filename = NULL;
  12. gchar *save_as_fullpath = NULL;
  13. guint count = 0;
  14. if (!g_file_test(save_in_path, G_FILE_TEST_IS_DIR)) {
  15. /* If the destination directory (or its parents) do not exist, create them. */
  16. if (g_mkdir_with_parents(save_in_path, 0755) == -1) {
  17. fprintf(stderr, "Failed to create export objects output directory \"%s\": %s\n",
  18. save_in_path, g_strerror(errno));
  19. return;
  20. }
  21. }
  22. while (slist) {
  23. entry = (export_object_entry_t *)slist->data;
  24. do {
  25. g_free(save_as_fullpath);
  26. if (entry->filename) {
  27. safe_filename = eo_massage_str(entry->filename,
  28. EXPORT_OBJECT_MAXFILELEN, count);
  29. } else {
  30. char generic_name[EXPORT_OBJECT_MAXFILELEN+1];
  31. const char *ext;
  32. ext = eo_ct2ext(entry->content_type);
  33. g_snprintf(generic_name, sizeof(generic_name),
  34. "object%u%s%s", entry->pkt_num, ext ? "." : "", ext ? ext : "");
  35. safe_filename = eo_massage_str(generic_name,
  36. EXPORT_OBJECT_MAXFILELEN, count);
  37. }
  38. save_as_fullpath = g_build_filename(save_in_path, safe_filename->str, NULL);
  39. g_string_free(safe_filename, TRUE);
  40. } while (g_file_test(save_as_fullpath, G_FILE_TEST_EXISTS) && ++count < prefs.gui_max_export_objects);
  41. count = 0;
  42. eo_save_entry(save_as_fullpath, entry);
  43. g_free(save_as_fullpath);
  44. save_as_fullpath = NULL;
  45. slist = slist->next;
  46. }
  47. }

此函数遍历导出数据对象链表, 对每一项执行导出文件名构造, 写入文件等事务.

整体流程

图例:

  • 实线箭头: 一般流程, A->B
  • 虚线箭头: 函数嵌套调用等, A contains B
  • 黄色背景: 相关的数据结构

eo_flow.png

参考

  • Wireshark源码: epan/export_object.h
  • Wireshark源码: ui/cli/tap-exportobject.h
  • Wireshark源码: epan/dissectors/packet-http.c
  • Wireshark源码: tshark.c
  • Wireshark原理: tap